




Здесь - это заархивированная статья Джима Мишела из InformIt, которая обрабатывает сортировку как для прямоугольных, так и для зубчатых многомерных массивов.
Я понимаю, о чем вы говорите - я уберу ошибочный пример, но оставлю ссылку на сортировочную статью. PS - спасибо, что сообщили мне причину голосования против. Вы не часто видите это, но это действительно конструктивно!
Ссылка не работает. Он перенаправляет на домашнюю страницу веб-сайта.
Спасибо KFL, я отредактировал ссылку, чтобы указать на архивную версию. Как бы я ни предпочел разместить для них ссылку на текущую версию на сайте InformIT, я не могу легко найти этот контент. Вроде убрали.
Загрузите свой двумерный массив строк в фактический 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#.
Звучит интересно, не могли бы вы опубликовать или связать несколько примеров кода, пожалуйста.
Сделанный. Может где-то баг - записал в блокнот.
Удивительно, что вы написали это в блокноте - во всяком случае, очень хорошо сработало. Спасибо.
Итак, ваш массив структурирован следующим образом (я буду говорить псевдокодом, потому что мой 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 comparer.Compare(y[col],x[col])) (там обратные x и y)
Попробуйте это. Основная стратегия состоит в том, чтобы независимо отсортировать конкретный столбец и запомнить исходную строку записи. Остальная часть кода будет циклически перебирать отсортированные данные столбца и заменять строки в массиве. Сложная часть не забыть обновить исходный столбец, поскольку часть подкачки эффективно изменяет исходный столбец.
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 (), как вы писали? Спасибо.
Это старый вопрос, но вот класс, который я только что построил, на основе статья Джима Мишеля в 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: У тебя есть хорошее предложение. Не могли бы вы сказать мне, будет ли эта строка кода сначала отсортировать весь массив по первому индексу, а затем отсортировать весь массив по второму индексу массива? Требуется ли для этого «использование System.Linq»? Спасибо.
В этом примере массив фактически не сортируется; LINQ создаст отсортированную последовательность, но только если вы зафиксируете результат ... он не сортирует существующий массив. Это может быть просто: string [] names = {"Смит", "Снайдер", "Бейкер", "Джонсон", "Баллмер"}; Array.Sort (имена);