2010-07-28 3 views
6

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

Например:

У меня есть 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"}; 

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

Чтобы сделать это, я переместил элемент 0 до конца.

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

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

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

ответ

2

Сначала вы должны создать объект 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; 
    } 
); 
+0

Таким образом, я повторяю весь список и создаю структуру данных из данных. Затем я сортирую данные, затем возвращаю их обратно в списки. Это имеет смысл, но это не кажется эффективным. Это, по крайней мере, будет работать, и это лучше, чем у меня в настоящее время. Благодарю. Изменить: я не знаю, что есть 3 элемента, поэтому я не могу использовать структуру. Возможно, список мог бы работать, но мне нужно будет беспокоиться о том, чтобы кастинг на большой сорт? –

+0

@John - Получаете ли вы эти списки из неконтролируемого источника? – ChaosPandion

+0

Да, список <> объекты, которые я получаю, не могут быть изменены; они могут быть отсортированы на месте, хотя. –

5

Я обработал этот проект в прошлом, сохранив или создав отдельный индексный список. Сначала вы сортируете индексный список, а затем используете его для сортировки (или просто доступа) к другим спискам. Вы можете сделать это, создав пользовательский 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]) 
} 

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

+1

+1. Это отличный подход. У меня есть именно такой «косвенный список» в моей личной библиотеке; это полезно для многих вещей. Пример кода [здесь] (http://nitolinq.codeplex.com/SourceControl/changeset/view/57003#1161747). –

1

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

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

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

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

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

3

Прошу прощения, но это похоже на плохой дизайн. Тем более, что список <T> не гарантирует порядок элементов, прежде чем вы назвали одну из сортировочных операций (так у вас есть проблемы при вставке):

From MSDN:

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

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

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

+0

Я согласен, что есть лучшие проекты. Основные ситуации, в которых я столкнулся с этим в прошлом, - это то, где у меня не было собственности на оригинальные массивы, и они были слишком большими, чтобы создавать отдельные копии. Если у вас есть право собственности, обязательно перефразируйте это в класс или структуру IComparable со всеми данными. – TechNeilogy

+0

Я не могу это изменить, но вот приложение. В основном объекты List <> содержат данные и данные передаются математическим функциям. Затем математические функции передают результат назад и присоединяются к классу таблицы. Было бы лучше хранить этот материал по-разному и проецировать столбцы с помощью LINQ? Это делает вставку намного сложнее. –

+0

Да, я, конечно, думаю, что лучше было бы хранить поровну. Я не вижу, как вставки будут более сложными; но опять же, я не знаю о всем приложении и ваших ограничениях. – driis

0

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

one = one.Sort(delegate(DateTime d1, DateTime d2) 
{ 
    return Convert.ToDateTime(d2).CompareTo(Convert.ToDateTime(d1)); 
}); 
1

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

В качестве альтернативы, если копирование данных не является огромной проблемой, вы можете проецировать его в запрос 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); 

Естественно, лучшим решением является не разделение связанных данных в первую очередь.

2

Вот как это сделать, используя 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(); 
+0

Мне нравится это решение, но эффективно ли оно? Я не так много знаю о производительности prjection; –

+0

Только один способ узнать - :-) –

Смежные вопросы