Подтвердить что ты не робот

Как отсортировать двумерный массив в С#?

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

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

4b9b3361

Ответ 1

Загрузите двумерный массив строк в фактический 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 заставил вас начать изучение лучшего способа обработки таблиц данных в приложении С#.

Ответ 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];
        }
    }
}

Ответ 3

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

Ответ 4

Этот код должен делать то, что вам нужно, я не обобщил его для 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]));
}

Ответ 5

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();
            }
        }
    }
}

Ответ 7

Итак, ваш массив структурирован таким образом (я буду говорить в псевдокоде, потому что мой С# -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]

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

Ответ 8

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


        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);
        }

Ответ 9

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

Ответ 10

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

В приведенном ниже примере я сначала загрузил некоторые несортированные целочисленные данные в tblToBeSorted в песочнице (не показан). Когда таблица и ее данные уже существуют, я загружаю ее (unsorted) в массив целых чисел 2D, а затем в DataTable. Массив DataRows - это отсортированная версия DataTable. Пример немного странный в том, что я загружаю свой массив из БД и мог его отсортировать, но я просто хотел получить несортированный массив на С# для использования с объектом 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();
    }
}

Ответ 11

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

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]).

Ответ 12

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

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

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

и вы хотите преобразовать его по номеру coloumn 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 coloumn 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;
}
}

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