C # Сортировать список, а также вернуть исходные позиции индекса? - PullRequest
30 голосов
/ 19 ноября 2009

Меня интересует сортировка коллекции, а также возвращение индекса, который можно использовать для сопоставления с исходной позицией в коллекции (до сортировки).

Позвольте мне привести пример, который будет более ясным:

List<int> A = new List<int>(){3,2,1};
List<int> B;
List<int> idx;

Sort(A,out B,out idx);

После чего:

A = [3,2,1] 
B = [1,2,3]
idx = [2,1,0]

Так что отношения между A, B, idx таковы:

A[i] == B[ idx[i] ], для i = 0 ... 2

Имеет ли C # /. Net какой-либо встроенный механизм, облегчающий реализацию?

Спасибо.

Ответы [ 4 ]

48 голосов
/ 19 ноября 2009

Это можно сделать довольно легко, используя Linq.

  • Преобразование вашего списка в новый список пар (объект, исходный индекс объекта).
  • Сортировка нового списка по первому элементу в паре
  • Извлечение отсортированного списка и исходных индексов.

Вот код, демонстрирующий принцип:

List<int> A = new List<int>() { 3, 2, 1 };

var sorted = A
    .Select((x, i) => new KeyValuePair<int, int>(x, i))
    .OrderBy(x => x.Key)
    .ToList();

List<int> B = sorted.Select(x => x.Key).ToList();
List<int> idx = sorted.Select(x => x.Value).ToList();

Я думаю, что это дает A [idx [i]] = B [i], но, надеюсь, этого достаточно для вас.

18 голосов
/ 19 ноября 2009

Хотя Марк Байерс предоставил вам решение с использованием LINQ, я хочу показать вам другое решение с использованием .NET Framework.

Существует перегрузка Array.Sort, которая сделает это за вас:

int[] a = new[] { 3, 2, 1 };
int[] p = new[] { 0, 1, 2 };

Array.Sort(a, p);

Assert.IsTrue(a.SequenceEquals(new[] { 1, 2, 3 }));
Assert.IsTrue(p.SequenceEquals(new[] { 2, 1, 0 }));

Таким образом, вот универсальный метод, соответствующий вашей спецификации, который использует эту перегрузку:

void Sort<T>(
    List<T> input,
    out List<T> output,
    out List<int> permutation,
    IComparer<T> comparer
) {
    if(input == null) { throw new ArgumentNullException("input"); }
    if(input.Count == 0) {
        // give back empty lists
        output = new List<T>(); 
        permutation = new List<int>();
        return;
    }
    if(comparer == null) { throw new ArgumentNullException("comparer"); }
    int[] items = Enumerable.Range(0, input.Count).ToArray();
    T[] keys = input.ToArray();
    Array.Sort(keys, items, comparer);
    output = keys.ToList();
    permutation = items.ToList();   
}
5 голосов
/ 30 января 2015

несколько более элегантный подход с использованием лямбды

Array.Sort<int>(idx, (a, b) => A[a].CompareTo(A[b]));

это дает массив idx из массива A

1 голос
/ 26 ноября 2018

На данный момент вы также можете использовать анонимные типы или кортежи значений вместо KeyValuePair. Это обеспечит более точное именование и сделает ваш код более читабельным:

Анонимные типы (C # 3.0):

List<int> arr = new List<int>() { 3, 2, 1 };

var sorted = arr
    .Select((x, i) => new { Value = x, OriginalIndex = i }))
    .OrderBy(x => x.Value)
    .ToList();

int originalIndexOfTheSmallestItem = sorted[0].OriginalIndex;

List<int> B = sorted.Select(x => x.Value).ToList();
List<int> idx = sorted.Select(x => x.OriginalIndex).ToList();

Значение кортежей (C # 7.0):

List<int> arr = new List<int>() { 3, 2, 1 };

var sorted = arr
    .Select((x, i) => (Value: x, OriginalIndex: i))
    .OrderBy(x => x.Value)
    .ToList();

int originalIndexOfTheSmallestItem = sorted[0].OriginalIndex;

List<int> B = sorted.Select(x => x.Value).ToList();
List<int> idx = sorted.Select(x => x.OriginalIndex).ToList();   

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...