Список сортировки C # на основе другого списка - PullRequest
6 голосов
/ 28 июля 2010

У меня есть класс, в котором есть несколько List <>.В основном это таблица, хранящаяся с каждым столбцом в виде списка <>.Каждый столбец не содержит один и тот же тип.Каждый список также имеет одинаковую длину (имеет одинаковое количество элементов).

Например:

У меня есть 3 объекта List <>;один список, два списка и три списка.

//Not syntactically correct
List<DateTime> one = new List...{4/12/2010, 4/9/2006, 4/13/2008};
List<double> two = new List...{24.5, 56.2, 47.4};
List<string> three = new List...{"B", "K", "Z"};

Я хочу иметь возможность сортировать список один от самого старого до нового: один = {4/9/2006, 13/03/2008, 4 /12/2010};

Поэтому для этого я переместил элемент 0 в конец.

Затем я хочу отсортировать список два и три одинаково;перемещение первого к последнему.

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

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

Ответы [ 7 ]

5 голосов
/ 28 июля 2010

Я занимался этим дизайном в прошлом, сохраняя или создавая отдельный список индексов. Сначала вы сортируете индексный список, а затем используете его для сортировки (или просто доступа) к другим спискам. Вы можете сделать это, создав собственный IComparer для списка индексов. То, что вы делаете внутри этого IComparer, - это сравнение на основе индексов в списке ключей. Другими словами, вы сортируете индексный список косвенно. Что-то вроде:

// This is the compare function for the separate *index* list.
int Compare (object x, object y)
{
  KeyList[(int) x].CompareTo(KeyList[(int) y])
}

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

3 голосов
/ 28 июля 2010

Вот способ сделать это, используя LINQ и проекции. Первый запрос генерирует массив с исходными индексами, упорядоченными по значениям datetime; в вашем примере массив newOrdering будет иметь члены:

{ 4/9/2006, 1 }, { 4/13/2008, 2 }, { 4/12/2010, 0 }

Второй набор операторов генерирует новые списки, выбирая элементы, используя переупорядоченные индексы (другими словами, элементы 1, 2 и 0 в этом порядке).

var newOrdering = one
    .Select((dateTime, index) => new { dateTime, index })
    .OrderBy(item => item.dateTime)
    .ToArray();

// now, order each list
one = newOrdering.Select(item => one[item.index]).ToList();
two = newOrdering.Select(item => two[item.index]).ToList();
three = newOrdering.Select(item => three[item.index]).ToList();
3 голосов
/ 28 июля 2010

Извините, но это похоже на плохой дизайн. Тем более что List не гарантирует порядок элементов до того, как вы вызвали одну из операций сортировки (поэтому у вас возникла проблема при вставке):

От MSDN :

Список не гарантируется отсортирован. Вы должны отсортировать список перед выполнением операций (таких как BinarySearch), которые требуют список быть отсортированным.

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

Я бы серьезно подумал о рефакторинге для использования другой структуры данных . Если вы все еще хотите реализовать сортировку на основе этой структуры данных, я бы создал временный объект (возможно, используя анонимный тип), отсортировал его и заново создал списки ( см. Этот превосходный ответ для объяснения). как).

2 голосов
/ 28 июля 2010

Сначала вы должны создать объект Data для хранения всего.

private class Data
{
    public DateTime DateTime { get; set; }
    public int Int32 { get; set; }
    public string String { get; set; }
}

Затем вы можете сортировать следующим образом.

var l = new List<Data>();
l.Sort(
    (a, b) =>
    {
        var r = a.DateTime.CompareTo(b);
        if (r == 0)
        {
            r = a.Int32.CompareTo(b);
            if (r == 0)
            {
                r = a.String.CompareTo(b);
            }
        }
        return r;
    }
);
1 голос
/ 28 июля 2010

Я написал алгоритм сортировки, который делает это для Nito.LINQ (еще не выпущен).Он использует простую быструю сортировку для сортировки списков и синхронизирует любое количество связанных списков. Исходный код начинается здесь, в методе расширения IList<T>.Sort.

В качестве альтернативы, если копирование данных не представляет большой проблемы, вы можете спроецировать его в запрос LINQ с помощью оператора Zip.(требуется .NET 4.0 или Rx), закажите его, а затем извлеките каждый результат:

List<DateTime> one = ...;
List<double> two = ...;
List<string> three = ...;
var combined = one.Zip(two, (first, second) => new { first, second })
    .Zip(three, (pair, third) => new { pair.first, pair.second, third });
var ordered = combined.OrderBy(x => x.first);
var orderedOne = ordered.Select(x => x.first);
var orderedTwo = ordered.Select(x => x.second);
var orderedThree = ordered.Select(x => x.third);

Естественно, лучшее решение - это не разделять связанные данные.

1 голос
/ 28 июля 2010

Использование универсальных массивов может быть немного громоздким.

Одной из альтернатив является использование метода Array.Sort(), который принимает массив ключей и массив значений для сортировки. Сначала он сортирует массив ключей в порядке возрастания и обеспечивает реорганизацию массива значений в соответствии с этим порядком сортировки.

Если вы готовы нести расходы на преобразование ваших List<T> s в массивы (а затем обратно), вы можете воспользоваться этим методом.

В качестве альтернативы, вы можете использовать LINQ для объединения значений из нескольких массивов в один анонимный тип, используя Zip(), отсортировать список анонимных типов по ключевому полю и затем разбить его на отдельные массивы.

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

0 голосов
/ 28 июля 2010

Я надеюсь, что это может помочь:

one = one.Sort(delegate(DateTime d1, DateTime d2)
{
    return Convert.ToDateTime(d2).CompareTo(Convert.ToDateTime(d1));
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...