2010-08-05 1 views
12

У меня есть набор элементов/ключей, которые я читаю из двух разных конфигурационных файлов. Таким образом, ключи могут быть одинаковыми, но с разными значениями, связанными с каждым из них.Как отсортировать список с помощью дубликатов ключей?

Я хочу привести их в отсортированном виде. Что я могу сделать ? Я пробовал с классом SortedList, но он не позволяет дублировать ключи.

Как я могу это сделать?

например Скажем, у меня есть 3 элемента с ключами 1,2,3. Затем я получаю еще один элемент, имеющий ключ 2 (но другое значение). Затем я хочу, чтобы новый ключ вставлен после существующего ключа 2, но до 3. Если я найду элемент с ключом 2, тогда он должен идти после последнего добавленного ключа 2.

Обратите внимание, что я использую. NET 2.0

+0

Вы действительно заботитесь, идут ли элементы с одинаковыми ключами до или после существующих элементов? – BlueMonkMN

+0

Да. Я хочу сохранить заказ, как указано в моем вопросе – Learner

ответ

12

Я предпочитаю использовать LINQ для этого типа вещи:

using System.Linq; 

... 

var mySortedList = myList.Orderby(l => l.Key) 
         .ThenBy(l => l.Value); 

foreach (var sortedItem in mySortedList) { 
    //You'd see each item in the order you specified in the loop here. 
} 

Примечание: необходимо использовать .NET 3.5 или более поздней версии для достижения этой цели.

+0

Спасибо, но я использую .NET 2.0 – Learner

+0

Yuck. Это само по себе достаточно для обновления. –

+1

это невозможно при использовании .net 2.0, поэтому не ответ на его вопрос – Nealv

1

.NET не имеет огромной поддержки для устойчивых видов (что означает, что эквивалентные элементы сохраняют свой относительный порядок при сортировке). Однако вы можете написать свою собственную стабильную сортировку-вставку, используя List.BinarySearch и пользовательский IComparer<T> (который возвращает -1, если ключ меньше или равен цели, а +1, если больше).

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

9

что вам нужно - функция сортировки с пользовательским IComparer. Теперь вы используете icomparer по умолчанию при использовании сортировки. это проверит значение поля.

При создании пользовательского IComparer (вы делаете это в своем классе, реализуя интерфейс Icomparable). что он делает: ваш объект проверяет себя на каждый другой объект в отсортированном вами списке.

это делается функцией. (не волнуйтесь, VS выполнит его при обращении к вашему интерфейсу

public class ThisObjectCLass : IComparable{ 

    public int CompareTo(object obj) { 
      ThisObjectCLass something = obj as ThisObjectCLass ; 
      if (something!= null) 
       if(this.key.CompareTo(object.key) == 0){ 
       //then: 
        if ..... 
       } 
       else if(this.value "is more important then(use some logic here)" something.value){ 
       return 1 
       } 
       else return -1 
      else 
       throw new ArgumentException("I am a dumb little rabid, trying to compare different base classes"); 
     } 
} 

прочитайте на приведенных выше ссылках для получения более подробной информации.

Я знаю, что у меня были некоторые проблемы с пониманием это сам в начале, так и для любой дополнительной помощи добавить комментарий, и я остановлюсь

2

Если вы на самом деле не заботиться о последовательности элементов с одинаковыми ключами, добавить все к списку, а затем отсортировать его по ключу:

static void Main(string[] args) 
{ 
    List<KeyValuePair<int, MyClass>> sortedList = 
     new List<KeyValuePair<int, MyClass>>() { 
     new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven")), 
     new KeyValuePair<int, MyClass>(5, new MyClass("five")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-b")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) 
     }; 
    sortedList.Sort(Compare); 
} 
static int Compare(KeyValuePair<int, MyClass> a, KeyValuePair<int, MyClass> b) 
{ 
    return a.Key.CompareTo(b.Key); 
} 

Если вы действительно хотите детали, вставленные позже, чтобы быть после того, как вставленные ранее, сортировать их, как они установлены:

class Sorter : IComparer<KeyValuePair<int, MyClass>> 
{ 

static void Main(string[] args) 
{ 
    List<KeyValuePair<int, MyClass>> sortedList = new List<KeyValuePair<int, MyClass>>(); 
    Sorter sorter = new Sorter(); 
    foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] { 
     new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven")), 
     new KeyValuePair<int, MyClass>(5, new MyClass("five")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-b")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-c")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) }) 
    { 
     sorter.Insert(sortedList, kv); 
    } 
    for (int i = 0; i < sortedList.Count; i++) 
    { 
     Console.WriteLine(sortedList[i].ToString()); 
    } 
} 
void Insert(List<KeyValuePair<int, MyClass>> sortedList, KeyValuePair<int, MyClass> newItem) 
{ 
    int newIndex = sortedList.BinarySearch(newItem, this); 
    if (newIndex < 0) 
     sortedList.Insert(~newIndex, newItem); 
    else 
    { 
     while (newIndex < sortedList.Count && (sortedList[newIndex].Key == newItem.Key)) 
     newIndex++; 
     sortedList.Insert(newIndex, newItem); 
    } 
} 
#region IComparer<KeyValuePair<int,MyClass>> Members 

public int Compare(KeyValuePair<int, MyClass> x, KeyValuePair<int, MyClass> y) 
{ 
    return x.Key.CompareTo(y.Key); 
} 

#endregion 
} 

Или вы могли бы отсортированный список списков:

static void Main(string[] args) 
{ 
    SortedDictionary<int, List<MyClass>> sortedList = new SortedDictionary<int,List<MyClass>>(); 
    foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] { 
     new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven")), 
     new KeyValuePair<int, MyClass>(5, new MyClass("five")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-b")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-c")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) }) 
    { 
     List<MyClass> bucket; 
     if (!sortedList.TryGetValue(kv.Key, out bucket)) 
     sortedList[kv.Key] = bucket = new List<MyClass>(); 
     bucket.Add(kv.Value); 
    } 
    foreach(KeyValuePair<int, List<MyClass>> kv in sortedList) 
    { 
     for (int i = 0; i < kv.Value.Count; i++) 
     Console.WriteLine(kv.Value[i].ToString()); 
    } 
} 

я не уверен, если вы можете использовать Список инициализаторами в .NET 2.0, как я сделал в первом примере выше, но я уверен, что вы знаете, как заполнить список данными.

0

Вы рассматривали класс NameValueCollection, так как он позволяет хранить несколько значений на ключ? вы могли бы иметь, например, следующее:

NameValueCollection nvc = new NameValueCollection(); 
    nvc.Add("1", "one"); 
    nvc.Add("2", "two"); 
    nvc.Add("3", "three"); 

    nvc.Add("2", "another value for two"); 
    nvc.Add("1", "one bis"); 

, а затем, чтобы получить значения, которые вы могли бы:

for (int i = 0; i < nvc.Count; i++) 
    { 
     if (nvc.GetValues(i).Length > 1) 
     { 
      for (int x = 0; x < nvc.GetValues(i).Length; x++) 
      { 
       Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i).GetValue(x)); 
      } 
     } 
     else 
     { 
      Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i)[0]); 
     } 

    } 

, которые дают выход:

'1' = 'один'

'1' = '' одним-бис

'2' = 'два'

'2' = 'другое значение для двух'

'3' 'три'

7

Я сделал это путем создания . Всякий раз, когда я нахожу дубликат, я просто вставляю значение в существующий список, связанный с ключом, уже присутствующим в объекте SortedList. Таким образом, у меня может быть список значений для определенного ключа.

+2

это довольно невероятно, что нет фактического отсортированного списка в C# ... –

+0

@ BlueRaja-DannyPflughoeft: есть 'SortedList', но он не позволяет дублировать ключи. Обратите внимание, что мои вопросы касались .NET 2.0. В любом случае, начиная с .NET 3.5, эта же проблема может быть решена с помощью 'Lookup' в Linq. См. Эту ссылку - http://msdn.microsoft.com/en-us/library/bb460184.aspx. – Learner

+3

Мне известно о SortedList и Lookup. Но это обе карты, а не списки. В C# нет фактического отсортированного списка. Существует «List.Sort()», но затем вставка-сортировка списка - это операция «O (n log n)», тогда как она должна быть просто «O (log n)» или «O (n)», в худшем случае. –

0

В .NET 2.0 вы можете написать:

List<KeyValuePair<string, string>> keyValueList = new List<KeyValuePair<string, string>>(); 

// Simulate your list of key/value pair which key could be duplicate 
keyValueList.Add(new KeyValuePair<string,string>("1","One")); 
keyValueList.Add(new KeyValuePair<string,string>("2","Two")); 
keyValueList.Add(new KeyValuePair<string,string>("3","Three")); 

// Here an entry with duplicate key and new value 
keyValueList.Add(new KeyValuePair<string, string>("2", "NEW TWO")); 

// Your final sorted list with one unique key 
SortedList<string, string> sortedList = new SortedList<string, string>(); 

foreach (KeyValuePair<string, string> s in keyValueList) 
{ 
    // Use the Indexer instead of Add method 
    sortedList[s.Key] = s.Value; 
} 

Выход:

[1, One] 
[2, NEW TWO] 
[3, Three] 
1

Как об этом

 SortedList<string, List<string>> sl = new SortedList<string, List<string>>(); 

     List<string> x = new List<string>(); 

     x.Add("5"); 
     x.Add("1"); 
     x.Add("5"); 
     // use this to load 
     foreach (string z in x) 
     { 
      if (!sl.TryGetValue(z, out x)) 
      { 
       sl.Add(z, new List<string>()); 
      } 

      sl[z].Add("F"+z); 
     } 
     // use this to print 
     foreach (string key in sl.Keys) 
     { 
      Console.Write("key=" + key + Environment.NewLine); 

      foreach (string item in sl[key]) 
      { 
       Console.WriteLine(item); 
      } 
     } 
+0

Спасибо, что посмотрели на вопрос. Но 'SortedList' бесполезен. Как уже упоминалось в самом вопросе, по какой-то причине у меня будут дублирующие ключи, а 'SortedList' не позволяет дублировать ключи. – Learner

+0

@CSharpLearner, этот ответ не использует повторяющиеся ключи. Элементы с дублирующимися ключами добавляются в список, поэтому, если вы перебираете ключи, вы получаете список уникальных ключей. Для любого ключа у вас может быть 1 или более значений, потому что каждое значение представляет собой список сам по себе. Это значение SortedList of List для значений. –

5

Используйте свой собственный класс компаратором! Если ваши ключи в отсортированном списке являются целыми числами, вы можете использовать, например, этот компаратор:

public class DegreeComparer : IComparer<int> 
{ 
    #region IComparer<int> Members 

    public int Compare(int x, int y) 
    { 
     if (x < y) 
      return -1; 
     else 
      return 1; 
    } 

    #endregion 
} 

создать экземпляр нового SortedList с ИНТ ключей и строковых значений используйте:

var mySortedList = new SortedList<int, string>(new DegreeComparer()); 
0

Я был аналогичная проблема, когда я разрабатывал игру, похожую на концепцию шахматной игры, в которой у вас есть компьютер.Мне нужно было иметь возможность сделать несколько кусков, чтобы сделать ход, и поэтому мне нужно было иметь несколько штатов Советов. Каждому члену правления необходимо было занять место на основе позиции частей. Для аргументации и простоты, скажем, моя игра была Noughts and Crosses, и я был Noughts, а Computer был Crosses. Если состояние платы показывало 3 строки Noughts, то это лучшее состояние для меня, если оно показывает 3 в ряду крестов, то это худшее состояние для меня и лучше всего подходит для компьютера. Во время игры есть другие состояния, которые более удобны для одного или другого, и кроме того, есть многословные состояния, которые приводят к рисованию, так как я могу его ранжировать, когда есть равные оценки ранга. Это то, что я придумал (извинись заранее, если вы не программист VB).

Мой класс Comparer:

Class ByRankScoreComparer 
    Implements IComparer(Of BoardState) 

    Public Function Compare(ByVal bs1 As BoardState, ByVal bs2 As BoardState) As Integer Implements IComparer(Of BoardState).Compare 
     Dim result As Integer = bs2.RankScore.CompareTo(bs1.RankScore) 'DESCENDING order 
     If result = 0 Then 
      result = bs1.Index.CompareTo(bs2.Index) 
     End If 
     Return result 
    End Function 
End Class 

Мои заявления:

Dim boardStates As SortedSet(Of BoardState)(New ByRankScoreComparer) 

Мой совет-State реализация:

Class BoardState 
    Private Shared BoardStateIndex As Integer = 0 
    Public ReadOnly Index As Integer 
    ... 
    Public Sub New() 
     BoardStateIndex += 1 
     Index = BoardStateIndex 
    End Sub 
    ... 
End Class 

Как вы можете видеть RankScores поддерживаются в порядке и любой нисходящий 2 состояния с одинаковым рангом-оценкой, более позднее состояние идет на дно, так как оно всегда будет иметь er присвоен Индекс, и, таким образом, это позволяет дублировать. Я также могу безопасно вызвать boardStates.Remove (myCurrentBoardState), который также использует компаратор, и компаратор должен вернуть значение 0, чтобы найти объект, подлежащий удалению.