Как отсортировать двумерный (прямоугольный) массив в C#?

У меня есть двумерный массив (строк), который составляет мою таблицу данных (строк и столбцов). Я хочу отсортировать этот массив по любому столбцу. Я пытался найти алгоритм для этого на C#, но безуспешно.

Любая помощь приветствуется.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
37
0
104 289
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Здесь - это заархивированная статья Джима Мишела из InformIt, которая обрабатывает сортировку как для прямоугольных, так и для зубчатых многомерных массивов.

В этом примере массив фактически не сортируется; LINQ создаст отсортированную последовательность, но только если вы зафиксируете результат ... он не сортирует существующий массив. Это может быть просто: string [] names = {"Смит", "Снайдер", "Бейкер", "Джонсон", "Баллмер"}; Array.Sort (имена);

Marc Gravell 24.10.2008 07:46

Я понимаю, о чем вы говорите - я уберу ошибочный пример, но оставлю ссылку на сортировочную статью. PS - спасибо, что сообщили мне причину голосования против. Вы не часто видите это, но это действительно конструктивно!

Doug L. 24.10.2008 07:58

Ссылка не работает. Он перенаправляет на домашнюю страницу веб-сайта.

KFL 22.01.2018 21:46

Спасибо KFL, я отредактировал ссылку, чтобы указать на архивную версию. Как бы я ни предпочел разместить для них ссылку на текущую версию на сайте InformIT, я не могу легко найти этот контент. Вроде убрали.

Doug L. 29.01.2018 07:17
Ответ принят как подходящий

Загрузите свой двумерный массив строк в фактический DataTable (System.Data.DataTable), а затем используйте метод Select () объекта DataTable для создания отсортированного массива объектов DataRow (или используйте DataView для аналогичного эффекта).

// assumes stringdata[row, col] is your 2D string array
DataTable dt = new DataTable();
// assumes first row contains column names:
for (int col = 0; col < stringdata.GetLength(1); col++)
{
    dt.Columns.Add(stringdata[0, col]);
}
// load data from string array to data table:
for (rowindex = 1; rowindex < stringdata.GetLength(0); rowindex++)
{
    DataRow row = dt.NewRow();
    for (int col = 0; col < stringdata.GetLength(1); col++)
    {
        row[col] = stringdata[rowindex, col];
    }
    dt.Rows.Add(row);
}
// sort by third column:
DataRow[] sortedrows = dt.Select("", "3");
// sort by column name, descending:
sortedrows = dt.Select("", "COLUMN3 DESC");

Вы также можете написать свой собственный метод для сортировки двумерного массива. Оба подхода были бы полезны для обучения, но подход DataTable поможет вам начать изучение лучшего способа обработки таблиц данных в приложении C#.

Звучит интересно, не могли бы вы опубликовать или связать несколько примеров кода, пожалуйста.

Jack 24.10.2008 07:59

Сделанный. Может где-то баг - записал в блокнот.

MusiGenesis 24.10.2008 08:15

Удивительно, что вы написали это в блокноте - во всяком случае, очень хорошо сработало. Спасибо.

Jack 24.10.2008 09:00

Итак, ваш массив структурирован следующим образом (я буду говорить псевдокодом, потому что мой C# -fu слабый, но я надеюсь, что вы уловили суть того, что я говорю)

string values[rows][columns]

Итак, value[1][3] - это значение в строке 1, столбце 3.

Вы хотите отсортировать по столбцам, поэтому проблема в том, что ваш массив отклонен на 90 градусов.

Не могли бы вы просто повернуть его в качестве первого разреза?

std::string values_by_column[columns][rows];

for (int i = 0; i < rows; i++)
  for (int j = 0; j < columns; j++)
    values_by_column[column][row] = values[row][column]

sort_array(values_by_column[column])

for (int i = 0; i < rows; i++)
  for (int j = 0; j < columns; j++)
    values[row][column] = values_by_column[column][row]

Если вы знаете, что хотите сортировать только один столбец за раз, вы можете значительно оптимизировать это, просто извлекая данные, которые вы хотите отсортировать:

  string values_to_sort[rows]
  for (int i = 0; i < rows; i++)
    values_to_sort[i] = values[i][column_to_sort]

  sort_array(values_to_sort)

  for (int i = 0; i < rows; i++)
    values[i][column_to_sort] = values_to_sort[i]

В C++ вы можете поиграть с тем, как вычислить смещения в массиве (поскольку вы можете рассматривать свой двумерный массив как одномерный массив), но я не уверен, как это сделать в C#.

Этот код должен делать то, что вам нужно, я не обобщал его для n на n, но это просто. Тем не менее - я согласен с MusiGenesis, используя другой объект, который немного лучше подходит для этого (особенно если вы собираетесь делать какие-либо привязки)

(Нашел код здесь)

string[][] array = new string[3][];

array[0] = new string[3] { "apple", "apple", "apple" };
array[1] = new string[3] { "banana", "banana", "dog" };
array[2] = new string[3] { "cat", "hippo", "cat" };         

for (int i = 0; i < 3; i++)
{
   Console.WriteLine(String.Format("{0} {1} {2}", array[i][0], array[i][1], array[i][2]));
}

int j = 2;

Array.Sort(array, delegate(object[] x, object[] y)
  {
    return (x[j] as IComparable).CompareTo(y[ j ]);
  }
);

for (int i = 0; i < 3; i++)
{
  Console.WriteLine(String.Format("{0} {1} {2}", array[i][0], array[i][1], array[i][2]));
}

Могу я проверить - вы имеете в виду прямоугольный массив ([,]) или зубчатый массив ([][])?

Сортировать зубчатый массив довольно просто; У меня есть обсуждение этого здесь. Очевидно, что в этом случае Comparison<T> будет использовать столбец вместо сортировки по порядковому номеру - но очень похоже.

Сортировка прямоугольного массива сложнее ... У меня, вероятно, возникнет соблазн скопировать данные либо в прямоугольный массив, либо в List<T[]>, и отсортировать там, а затем скопировать обратно.

Вот пример использования массива с зазубринами:

static void Main()
{  // could just as easily be string...
    int[][] data = new int[][] { 
        new int[] {1,2,3}, 
        new int[] {2,3,4}, 
        new int[] {2,4,1} 
    }; 
    Sort<int>(data, 2); 
} 
private static void Sort<T>(T[][] data, int col) 
{ 
    Comparer<T> comparer = Comparer<T>.Default;
    Array.Sort<T[]>(data, (x,y) => comparer.Compare(x[col],y[col])); 
} 

Для работы с прямоугольным массивом ... ну, вот код для переключения между ними на лету ...

static T[][] ToJagged<T>(this T[,] array) {
    int height = array.GetLength(0), width = array.GetLength(1);
    T[][] jagged = new T[height][];

    for (int i = 0; i < height; i++)
    {
        T[] row = new T[width];
        for (int j = 0; j < width; j++)
        {
            row[j] = array[i, j];
        }
        jagged[i] = row;
    }
    return jagged;
}
static T[,] ToRectangular<T>(this T[][] array)
{
    int height = array.Length, width = array[0].Length;
    T[,] rect = new T[height, width];
    for (int i = 0; i < height; i++)
    {
        T[] row = array[i];
        for (int j = 0; j < width; j++)
        {
            rect[i, j] = row[j];
        }
    }
    return rect;
}
// fill an existing rectangular array from a jagged array
static void WriteRows<T>(this T[,] array, params T[][] rows)
{
    for (int i = 0; i < rows.Length; i++)
    {
        T[] row = rows[i];
        for (int j = 0; j < row.Length; j++)
        {
            array[i, j] = row[j];
        }
    }
}

Какие изменения нужно сделать в методе Sort, чтобы отсортировать данные по убыванию.

Homam 24.01.2019 14:47

@Homam comparer.Compare(y[col],x[col])) (там обратные x и y)

Marc Gravell 24.01.2019 18:08

Попробуйте это. Основная стратегия состоит в том, чтобы независимо отсортировать конкретный столбец и запомнить исходную строку записи. Остальная часть кода будет циклически перебирать отсортированные данные столбца и заменять строки в массиве. Сложная часть не забыть обновить исходный столбец, поскольку часть подкачки эффективно изменяет исходный столбец.


        public class Pair<T> {
            public int Index;
            public T Value;
            public Pair(int i, T v) {
                Index = i;
                Value = v;
            }
        }
        static IEnumerable<Pair<T>> Iterate<T>(this IEnumerable<T> source) {
            int index = 0;
            foreach ( var cur in source) {
                yield return new Pair<T>(index,cur);
                index++;
            }
        }
        static void Sort2d(string[][] source, IComparer comp, int col) {
            var colValues = source.Iterate()
                .Select(x => new Pair<string>(x.Index,source[x.Index][col])).ToList();
            colValues.Sort((l,r) => comp.Compare(l.Value, r.Value));
            var temp = new string[source[0].Length];
            var rest = colValues.Iterate();
            while ( rest.Any() ) {
                var pair = rest.First();
                var cur = pair.Value;
                var i = pair.Index;
                if (i == cur.Index ) {
                    rest = rest.Skip(1);
                    continue;
                }

                Array.Copy(source[i], temp, temp.Length);
                Array.Copy(source[cur.Index], source[i], temp.Length);
                Array.Copy(temp, source[cur.Index], temp.Length);
                rest = rest.Skip(1);
                rest.Where(x => x.Value.Index == i).First().Value.Index = cur.Index;
            }
        }

        public static void Test1() {
            var source = new string[][] 
            {
                new string[]{ "foo", "bar", "4" },
                new string[] { "jack", "dog", "1" },
                new string[]{ "boy", "ball", "2" },
                new string[]{ "yellow", "green", "3" }
            };
            Sort2d(source, StringComparer.Ordinal, 2);
        }

Если бы вы могли получить данные в виде универсального кортежа при чтении или извлечении, это было бы намного проще; тогда вам просто нужно написать функцию сортировки, которая сравнивает желаемый столбец кортежа, и вы получите одномерный массив кортежей.

Мне нравится подход DataTable, предложенный MusiGenesis выше. Приятно то, что вы можете сортировать по любой допустимой строке SQL 'order by', которая использует имена столбцов, например «x, y desc, z» для 'порядок по x, y desc, z'. (FWIW, мне не удалось заставить его работать, используя порядковые номера столбцов, например, «3,2,1» для «порядка 3,2,1»). Я использовал только целые числа, но очевидно, что вы могли добавить данные смешанного типа в DataTable и отсортируйте его в любую сторону.

В приведенном ниже примере я сначала загрузил некоторые несортированные целочисленные данные в tblToBeSorted в песочнице (не показано). Когда таблица и ее данные уже существуют, я загружаю ее (несортированную) в двумерный целочисленный массив, а затем в DataTable. Массив DataRows - это отсортированная версия DataTable. Пример немного странный, поскольку я загружаю свой массив из БД и мог бы отсортировать его тогда, но я просто хотел получить несортированный массив в C# для использования с объектом DataTable.

static void Main(string[] args)
{
    SqlConnection cnnX = new SqlConnection("Data Source=r90jroughgarden\\;Initial Catalog=Sandbox;Integrated Security=True");
    SqlCommand cmdX = new SqlCommand("select * from tblToBeSorted", cnnX);
    cmdX.CommandType = CommandType.Text;
    SqlDataReader rdrX = null;
    if (cnnX.State == ConnectionState.Closed) cnnX.Open();

    int[,] aintSortingArray = new int[100, 4];     //i, elementid, planid, timeid

    try
    {
        //Load unsorted table data from DB to array
        rdrX = cmdX.ExecuteReader();
        if (!rdrX.HasRows) return;

        int i = -1;
        while (rdrX.Read() && i < 100)
        {
            i++;
            aintSortingArray[i, 0] = rdrX.GetInt32(0);
            aintSortingArray[i, 1] = rdrX.GetInt32(1);
            aintSortingArray[i, 2] = rdrX.GetInt32(2);
            aintSortingArray[i, 3] = rdrX.GetInt32(3);
        }
        rdrX.Close();

        DataTable dtblX = new DataTable();
        dtblX.Columns.Add("ChangeID");
        dtblX.Columns.Add("ElementID");
        dtblX.Columns.Add("PlanID");
        dtblX.Columns.Add("TimeID");
        for (int j = 0; j < i; j++)
        {
            DataRow drowX = dtblX.NewRow();
            for (int k = 0; k < 4; k++)
            {
                drowX[k] = aintSortingArray[j, k];
            }
            dtblX.Rows.Add(drowX);
        }

        DataRow[] adrowX = dtblX.Select("", "ElementID, PlanID, TimeID");
        adrowX = dtblX.Select("", "ElementID desc, PlanID asc, TimeID desc");

    }
    catch (Exception ex)
    {
        string strErrMsg = ex.Message;
    }
    finally
    {
        if (cnnX.State == ConnectionState.Open) cnnX.Close();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[,] arr = { { 20, 9, 11 }, { 30, 5, 6 } };
            Console.WriteLine("before");
            for (int i = 0; i < arr.GetLength(0); i++)
            {
                for (int j = 0; j < arr.GetLength(1); j++)
                {
                    Console.Write("{0,3}", arr[i, j]);
                }
                Console.WriteLine();
            }
            Console.WriteLine("After");

            for (int i = 0; i < arr.GetLength(0); i++) // Array Sorting
            {
                for (int j = arr.GetLength(1) - 1; j > 0; j--)
                {

                    for (int k = 0; k < j; k++)
                    {
                        if (arr[i, k] > arr[i, k + 1])
                        {
                            int temp = arr[i, k];
                            arr[i, k] = arr[i, k + 1];
                            arr[i, k + 1] = temp;
                        }
                    }
                }
                Console.WriteLine();
            }

            for (int i = 0; i < arr.GetLength(0); i++)
            {
                for (int j = 0; j < arr.GetLength(1); j++)
                {
                    Console.Write("{0,3}", arr[i, j]);
                }
                Console.WriteLine();
            }
        }
    }
}

Можно также посмотреть метод Array.Sort http://msdn.microsoft.com/en-us/library/aa311213(v=vs.71).aspx

например Array.Sort (массив, делегат (объект [] x, объект [] y) {return (x [i] как IComparable) .CompareTo (y [i]);});

из http://channel9.msdn.com/forums/Coffeehouse/189171-Sorting-Two-Dimensional-Arrays-in-C/

Привет, No_Nick, если у меня есть 2-мерный массив, и я хочу одновременно выполнить 2 сортировки по этому массиву: (1) Сначала отсортируйте по первому индексу; (2) Затем для всех элементов, которые имеют одинаковые значения для первого индекса, отсортируйте их по второму индексу; - Не могли бы вы рассказать мне, как это сделать с помощью Array.Sort (), как вы писали? Спасибо.

Job_September_2020 14.03.2021 23:50

Это старый вопрос, но вот класс, который я только что построил, на основе статья Джима Мишеля в InformIt, связанного Дуг Л.

class Array2DSort : IComparer<int>
{
    // maintain a reference to the 2-dimensional array being sorted
    string[,] _sortArray;
    int[] _tagArray;
    int _sortIndex;

    protected string[,] SortArray { get { return _sortArray; } }

    // constructor initializes the sortArray reference
    public Array2DSort(string[,] theArray, int sortIndex)
    {
        _sortArray = theArray;
        _tagArray = new int[_sortArray.GetLength(0)];
        for (int i = 0; i < _sortArray.GetLength(0); ++i) _tagArray[i] = i;
        _sortIndex = sortIndex;
    }

    public string[,] ToSortedArray()
    {
        Array.Sort(_tagArray, this);
        string[,] result = new string[
            _sortArray.GetLength(0), _sortArray.GetLength(1)];
        for (int i = 0; i < _sortArray.GetLength(0); i++)
        {
            for (int j = 0; j < _sortArray.GetLength(1); j++)
            {
                result[i, j] = _sortArray[_tagArray[i], j];
            }
        }
        return result;
    }

    // x and y are integer row numbers into the sortArray
    public virtual int Compare(int x, int y)
    {
        if (_sortIndex < 0) return 0;
        return CompareStrings(x, y, _sortIndex);
    }

    protected int CompareStrings(int x, int y, int col)
    {
        return _sortArray[x, col].CompareTo(_sortArray[y, col]);
    }
}

Учитывая несортированный 2D-массив data произвольного размера, который вы хотите отсортировать по столбцу 5, вы просто делаете это:

        Array2DSort comparer = new Array2DSort(data, 5);
        string[,] sortedData = comparer.ToSortedArray();

Обратите внимание на виртуальный метод Compare и защищенный SortArray, чтобы вы могли создавать специализированные подклассы, которые всегда сортируют по определенному столбцу или выполняют специализированную сортировку по нескольким столбцам, или что угодно. Вот почему CompareStrings разбит и защищен - любые подклассы могут использовать его для простых сравнений вместо того, чтобы печатать полный синтаксис SortArray[x, col].CompareTo(SortArray[y, col]).

Я знаю, что уже поздно, но вот моя мысль, которую вы могли бы рассмотреть.

например это массив

{
m,m,m
a,a,a
b,b,b
j,j,j
k,l,m
}

и вы хотите преобразовать его по столбцу номер 2, тогда

string[] newArr = new string[arr.length]
for(int a=0;a<arr.length;a++)
    newArr[a] = arr[a][1] + a;

// create new array that contains index number at the end and also the column values
Array.Sort(newArr);
for(int a=0;a<newArr.length;a++)
{
    int index = Convert.ToInt32(newArr[a][newArr[a].Length -1]);
    //swap whole row with tow at current index
    if (index != a)
    {
        string[] arr2 = arr[a];
        arr[a] = arr[index];
        arr[index] = arr2;
    }
}

Поздравляем, вы отсортировали массив по желаемому столбцу. Вы можете отредактировать его, чтобы он работал с другими типами данных.

Array.Sort (array, (a, b) => {return a [0] - b [0];});

Добро пожаловать в SO. Пожалуйста, не размещайте код, только ответы. Поскольку код прост для вас, другим может быть сложно понять его и почему вы использовали этот подход. Пожалуйста, поясните свой код, почему вы сделали это именно так. Также обратите внимание, что этот вопрос относится к 2008 году.

Korashen 23.11.2019 02:51

@Korashen: У тебя есть хорошее предложение. Не могли бы вы сказать мне, будет ли эта строка кода сначала отсортировать весь массив по первому индексу, а затем отсортировать весь массив по второму индексу массива? Требуется ли для этого «использование System.Linq»? Спасибо.

Job_September_2020 14.03.2021 23:36

Другие вопросы по теме