Подтвердить что ты не робот

Как отсортировать список с дублирующими ключами?

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

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

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

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

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

4b9b3361

Ответ 1

Я предпочитаю использовать 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 или новее.

Ответ 2

вам нужна функция Sort с пользовательским 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");
        }
}

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

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

Ответ 3

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

Ответ 4

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

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 с ключами int и строковыми значениями, используйте:

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

Ответ 5

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

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());
   }
}

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

Ответ 6

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

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

Ответ 7

Как насчет этого

        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);
            }
        }

Ответ 8

Вы рассматривали класс 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' = 'one'

'1' = 'one bis'

'2' = 'two'

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

'3' = 'three'

Ответ 9

В .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]

Ответ 10

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

Мой класс сравнения:

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)

Реализация моего правления:

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 состояния, имеющие одинаковый ранговый балл, более позднее состояние уходит в нижнее положение, так как оно всегда будет иметь больший назначенный Индекс и, таким образом, это позволяет дублировать. Я также могу безопасно вызвать boardStates.Remove(myCurrentBoardState), который также использует компаратор, и компаратор должен вернуть значение 0, чтобы найти объект, подлежащий удалению.