У меня есть класс, который имеет несколько Список <> содержавший в нем. В основном таблица, хранимая с каждым столбцом как Список <>. Каждый столбец не содержит тот же тип. Каждый список является также той же длиной (имеет то же число элементов).
Например:
У меня есть 3 Списка <> объекты; один Список, два Списка и три Списка.
//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"};
Я хочу смочь отсортировать список один от самых старых до новейших: один = {09.04.2006, 13.04.2008, 12.04.2010};
Таким образом, чтобы сделать это я переместил элемент 0 в конец.
Я затем хочу отсортировать список два и три тот же путь; перемещение первого к последнему.
Таким образом, когда я сортирую один список, я хочу, чтобы данные в соответствующем индексе в других списках также изменились в соответствии с тем, как один список отсортирован.
Я предполагаю, что должен перегрузить IComparer так или иначе, но я чувствую, что существует ярлык, который я не понял.
Раньше я занимался этим дизайном, сохраняя или создавая отдельный список индексов. Сначала вы сортируете список индекса, а затем используете его для сортировки (или просто доступа) к другим спискам. Вы можете сделать это, создав собственный 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])
}
Итак, вы сортируете список индексов на основе значений в списке ключей. Затем вы можете использовать этот отсортированный список ключей для изменения порядка других списков. Если это неясно, я постараюсь добавить более полный пример, когда у меня возникнет ситуация, чтобы опубликовать его.
Я написал алгоритм сортировки, который делает это для Nito.LINQ (еще не выпущен). Он использует простую QuickSort для сортировки списков и поддерживает синхронизацию любого количества связанных списков. Исходный код начинается здесь, в методе расширения IList
.
В качестве альтернативы, если копирование данных не представляет большой проблемы, вы можете спроецировать его в запрос 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);
Естественно, лучшее решение - в первую очередь не разделять связанные данные.
Сначала вы должны создать объект 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;
}
);
Надеюсь, это может помочь:
one = one.Sort(delegate(DateTime d1, DateTime d2)
{
return Convert.ToDateTime(d2).CompareTo(Convert.ToDateTime(d1));
});
Вот способ сделать это с помощью LINQ и проекций. Первый запрос генерирует массив с исходными индексами, переупорядоченными по значениям времени; в вашем примере массив 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();
Мне жаль, но это похоже на плохой дизайн. Особенно потому, что List
Из MSDN :
Список не гарантированно будет отсортировано. Вы должны отсортировать список перед выполнением операций (например, BinarySearch), которым требуется список быть отсортированным.
Во многих случаях вы не столкнетесь с проблемами, связанными с этим, но могут, и если вы это сделаете, то эту ошибку будет очень сложно отследить. Например, я думаю, что текущая реализация фреймворка List
Я бы серьезно рассмотрел возможность рефакторинга для использования другой структуры данных . Если вы все еще хотите реализовать сортировку на основе этой структуры данных, я бы создал временный объект (возможно, используя анонимный тип), отсортирую его и заново создам списки ( см. Этот отличный ответ для объяснения о том, как).
Использование универсальных массивов может стать немного громоздким.
Альтернативой является использование метода Array.Sort ()
, который принимает массив ключей и массив значений для сортировки. Сначала он сортирует массив ключей в порядке возрастания и обеспечивает реорганизацию массива значений в соответствии с этим порядком сортировки.
Если вы готовы взять на себя расходы по преобразованию ваших List
s в массивы (а затем обратно), вы можете воспользоваться этим методом.
В качестве альтернативы вы можете использовать LINQ для объединения значений из нескольких массивов в один анонимный тип с помощью Zip ()
, отсортировать список анонимных типов с помощью ключевого поля, а затем разделить его на отдельные массивы.
Если вы хотите сделать это на месте, вам придется написать собственный компаратор и создать отдельный массив индексов для поддержания нового порядка элементов.