Как вы сортируете словарь по значению?

608

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

Существует SortedList, который хорош для одного значения (например, частоты), что я хочу отнести его к слову.

SortedDictionary заказывает ключ, а не значение. Некоторые прибегают к пользовательскому классу, но есть ли более чистый способ?

  • 0
    Помимо простой сортировки словаря (как в принятом ответе), вы также можете просто создать IComparer который выполняет свою IComparer (правда, он принимает ключ для сравнения, но с ключом вы можете получить значение). ;-)
Теги:
sorting
dictionary

17 ответов

467
Лучший ответ

Использование:

using System.Linq.Enumerable;
...
List<KeyValuePair<string, string>> myList = aDictionary.ToList();

myList.Sort(
    delegate(KeyValuePair<string, string> pair1,
    KeyValuePair<string, string> pair2)
    {
        return pair1.Value.CompareTo(pair2.Value);
    }
);

Поскольку вы нацеливаете .NET 2.0 или выше, вы можете упростить это в синтаксисе лямбда - это эквивалентно, но короче. Если вы настроили таргетинг на .NET 2.0, вы можете использовать этот синтаксис только в том случае, если вы используете компилятор из Visual Studio 2008 (или выше).

var myList = aDictionary.ToList();

myList.Sort((pair1,pair2) => pair1.Value.CompareTo(pair2.Value));
  • 23
    Я использовал это решение (спасибо!), Но был в замешательстве в течение минуты, пока не прочитал пост Майкла Стума (и его фрагмент кода от Джона Тимни) и понял, что myList - это вторичный объект, список KeyValuePairs, который создается из словаря, а потом отсортировано.
  • 2
    извините, но этот ответ было трудно понять, так как я не знаком с ключевым словом делегата (может быть, оно отличается от vb), и не ясно, где происходит сортировка, так как вам нужно будет либо запустить количество элементов, умноженное на количество элементов (без элементов в квадрате) сравнения путем поиска / сравнения каждого элемента со всем словарем для каждого элемента, или если вы просто сравниваете текущий n в последний раз, то вам нужно сделать это более чем в одном цикле по коллекции, что Вот почему я не получил его как есть. может быть, больше информации о том, где происходит сортировка и переупорядочение, было бы полезно!
Показать ещё 7 комментариев
486

Используйте LINQ:

Dictionary<string, int> myDict = new Dictionary<string, int>();
myDict.Add("one", 1);
myDict.Add("four", 4);
myDict.Add("two", 2);
myDict.Add("three", 3);

var sortedDict = from entry in myDict orderby entry.Value ascending select entry;

Это также обеспечит большую гибкость в том, что вы можете выбрать лучшие 10, 20 10% и т.д. Или, если вы используете свой индекс частоты слов для type-ahead, вы также можете включить предложение StartsWith.

  • 13
    Как я могу изменить sortedDict обратно в словарь <string, int>? Выложен новый ТАК вопрос здесь: stackoverflow.com/questions/3066182/…
  • 1
    К сожалению, это не работает на VS2005 из-за .net Framework 2.0 там (без LINQ). Хорошо также иметь ответ Бамбрика.
Показать ещё 10 комментариев
169
var ordered = dict.OrderBy(x => x.Value);
  • 5
    Я не уверен, почему это решение не более популярно - возможно, потому что для него требуется .NET 3.5?
  • 23
    Это хорошее решение, но оно должно иметь это право перед конечной точкой с запятой: .ToDictionary (pair => pair.Key, pair => pair.Value);
Показать ещё 6 комментариев
138

Оглядываясь и используя некоторые функции С# 3.0, мы можем это сделать:

foreach (KeyValuePair<string,int> item in keywordCounts.OrderBy(key=> key.Value))
{ 
    // do something with item.Key and item.Value
}

Это самый чистый способ, который я видел и похож на рубиновый способ обработки хэшей.

  • 0
    Я пытался отсортировать словарь при добавлении KeyValuePairs в ComboBox ... это работало отлично! Спасибо!
  • 4
    Не забудьте добавить пространство имен System.Linq при использовании этого синтаксиса.
Показать ещё 3 комментария
130

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

dict = dict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);

Конечно, это может быть неправильно, но оно работает.

  • 7
    Вы также можете использовать OrderByDescending, если хотите отсортировать по убыванию.
  • 0
    Работал для меня, хотя мне пришлось немного изменить его на: Словарь <ключ, значение> dict = dict.OrderBy (x => x.Value) .ToDictionary (x => x.Key, x => x.Value);
Показать ещё 6 комментариев
53

На высоком уровне у вас нет другого выбора, чтобы пройти через весь Словарь и посмотреть на каждое значение.

Может быть, это помогает: http://bytes.com/forum/thread563638.html Копия/Вставка от Джона Тимни:

Dictionary<string, string> s = new Dictionary<string, string>();
s.Add("1", "a Item");
s.Add("2", "c Item");
s.Add("3", "b Item");

List<KeyValuePair<string, string>> myList = new List<KeyValuePair<string, string>>(s);
myList.Sort(
    delegate(KeyValuePair<string, string> firstPair,
    KeyValuePair<string, string> nextPair)
    {
        return firstPair.Value.CompareTo(nextPair.Value);
    }
);
  • 3
    stringnextPair -> строка> nextPair stringfirstPair -> строка> firstPair
  • 1
    Идеальное решение, отличное от Linq. Меня не перестает удивлять то, как люди чувствуют необходимость использовать Linq, даже когда это абсолютно не требуется для решения проблемы. Я полагаю, что в C # 3 вы также можете упростить сортировку, чтобы просто использовать лямбду: myList.Sort ((x, y) => x.Value.CompareTo (y.Value));
20

В любом случае вы никогда не сможете сортировать словарь. Они фактически не заказываются. Гарантии для словаря заключаются в том, что коллекции ключей и значений являются итерабельными, а значения могут быть получены индексом или ключом, но здесь нет гарантии какого-либо конкретного заказа. Следовательно, вам нужно будет ввести пару имени в список.

  • 1
    Сортированный словарь может дать список пар ключ-значение.
  • 1
    @recursive Любой словарь должен дать это. Интересно отметить, что мой ответ, который является правильным, но неполным (мог бы сделать то, что сделали лучшие примеры), голосуется ниже недопустимого ответа, что приведет к исключениям для дублирующихся значений в исходном словаре (ключи уникальны, значения не гарантируются быть)
Показать ещё 1 комментарий
14

Вы не сортируете записи в словаре. Класс словаря в .NET реализуется как хэш-таблица - эта структура данных по определению не сортируется.

Если вам нужно иметь возможность перебирать свою коллекцию (по ключу), вам нужно использовать SortedDictionary, который реализован как двоичное дерево поиска.

В вашем случае, однако, структура источника не имеет значения, поскольку она сортируется по другому полю. Вам все равно придется сортировать его по частоте и помещать в новую коллекцию, отсортированную по соответствующему полю (частоте). Поэтому в этой коллекции частоты - это клавиши, а слова - значения. Поскольку многие слова могут иметь одинаковую частоту (и вы собираетесь использовать ее в качестве ключа), вы не можете использовать ни словарь, ни SortedDictionary (для них требуются уникальные ключи). Это оставляет вас с SortedList.

Я не понимаю, почему вы настаиваете на поддержании ссылки на исходный элемент в вашем основном/первом словаре.

Если объекты в вашей коллекции имели более сложную структуру (больше полей), и вам нужно было иметь возможность эффективно обращаться к ним/сортировать их с помощью нескольких разных полей в качестве ключей. Возможно, вам понадобится настраиваемая структура данных, которая будет состоять из основное хранилище, которое поддерживает O (1) вставку и удаление (LinkedList) и несколько структур индексирования - Словари/Сортированные словари/Сортированные списки. Эти индексы будут использовать одно из полей вашего сложного класса в качестве ключа и указатель/ссылку на LinkedListNode в LinkedList в качестве значения.

Вам нужно будет координировать вставки и удаления, чтобы синхронизировать индексы с основной коллекцией (LinkedList), и удаление было бы довольно дорогостоящим, я думаю. Это похоже на то, как работают индексы базы данных - они являются фантастическими для поиска, но они становятся бременем, когда вам нужно выполнять множество операций и удаления.

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

var dict = new SortedDictionary<string, int>();
// ToDo: populate dict

var output = dict.OrderBy(e => e.Value).Select(e => new {frequency = e.Value, word = e.Key}).ToList();

foreach (var entry in output)
{
    Console.WriteLine("frequency:{0}, word: {1}",entry.frequency,entry.word);
}
10
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);
10

Сортировка значений

Показывает, как сортировать значения в словаре. Мы видим консольную программу, которую вы можете компилировать в Visual Studio и запускать. Он добавляет ключи к словарю, а затем сортирует их по их значениям. Помните, что экземпляры Dictionary первоначально не отсортированы. Мы используем ключевое слово ordering LINQ в запросе.

Пункт OrderBy Программа, которая сортирует словарь [С#]

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // Example dictionary.
        var dictionary = new Dictionary<string, int>(5);
        dictionary.Add("cat", 1);
        dictionary.Add("dog", 0);
        dictionary.Add("mouse", 5);
        dictionary.Add("eel", 3);
        dictionary.Add("programmer", 2);

        // Order by values.
        // ... Use LINQ to specify sorting by value.
        var items = from pair in dictionary
                orderby pair.Value ascending
                select pair;

        // Display results.
        foreach (KeyValuePair<string, int> pair in items)
        {
            Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
        }

        // Reverse sort.
        // ... Can be looped over in the same way as above.
        items = from pair in dictionary
        orderby pair.Value descending
        select pair;
    }
}

Выход

dog: 0
cat: 1
programmer: 2
eel: 3
mouse: 5
10

Или для удовольствия вы можете использовать некоторую доброту расширения LINQ:

var dictionary = new Dictionary<string, int> { { "c", 3 }, { "a", 1 }, { "b", 2 } };
dictionary.OrderBy(x => x.Value)
  .ForEach(x => Console.WriteLine("{0}={1}", x.Key,x.Value));
9

Сортировка списка SortedDictionary для привязки к элементу управления ListView с помощью VB.NET:

Dim MyDictionary As SortedDictionary(Of String, MyDictionaryEntry)

MyDictionaryListView.ItemsSource = MyDictionary.Values.OrderByDescending(Function(entry) entry.MyValue)

Public Class MyDictionaryEntry ' Need Property for GridViewColumn DisplayMemberBinding
    Public Property MyString As String
    Public Property MyValue As Integer
End Class

XAML:

<ListView Name="MyDictionaryListView">
    <ListView.View>
        <GridView>
            <GridViewColumn DisplayMemberBinding="{Binding Path=MyString}" Header="MyStringColumnName"></GridViewColumn>
            <GridViewColumn DisplayMemberBinding="{Binding Path=MyValue}" Header="MyValueColumnName"></GridViewColumn>
         </GridView>
    </ListView.View>
</ListView>
5

Другие ответы хороши, если все, что вы хотите, это иметь "временный" список, отсортированный по значению. Однако, если вы хотите иметь словарь, отсортированный по Key, который автоматически синхронизируется с другим словарем, отсортированным по Value, вы можете использовать Bijection<K1, K2> класс.

Bijection<K1, K2> позволяет вам инициализировать коллекцию двумя существующими словарями, поэтому, если вы хотите, чтобы один из них был несортирован, и вы хотите, чтобы другой был отсортирован, вы могли бы создать свою биекцию с кодом, например

var dict = new Bijection<Key, Value>(new Dictionary<Key,Value>(), 
                               new SortedDictionary<Value,Key>());

Вы можете использовать dict как любой обычный словарь (он реализует IDictionary<>), а затем вызывает dict.Inverse, чтобы получить "обратный" словарь, который сортируется по Value.

Bijection<K1, K2> является частью Loyc.Collections.dll, но если вы хотите, вы можете просто скопировать исходный код в ваш собственный проект.

Примечание. Если есть несколько ключей с одинаковым значением, вы не можете использовать Bijection, но вы можете вручную синхронизировать между обычными Dictionary<Key,Value> и a BMultiMap<Value,Key>.

  • 0
    Аналогично http://stackoverflow.com/questions/268321, но может заменить каждый словарь на SortedDictionary. Хотя ответы выглядят не для поддержки повторяющихся значений (предполагается от 1 до 1).
5

Самый простой способ получить отсортированный словарь - использовать встроенный класс SortedDictionary:

//Sorts sections according to the key value stored on "sections" unsorted dictionary, which is passed as a constructor argument
System.Collections.Generic.SortedDictionary<int, string> sortedSections = null;
if (sections != null)
{
    sortedSections = new SortedDictionary<int, string>(sections);
}

sortedSections будет содержать отсортированную версию sections

  • 5
    Как вы упомянули в своем комментарии, SortedDictionary сортирует по ключам. ОП хочет отсортировать по значению. SortedDictionary не помогает в этом случае.
  • 0
    Хорошо ... Если он / она (вы) можете, просто установите значения в качестве ключей. Я sorteddictionary() время выполнения операций и sorteddictionary() всегда выигрывал по крайней мере на 1 микросекунду, и им намного легче управлять (поскольку издержки преобразования его обратно во что-то, с чем легко взаимодействовать и управлять им, как в словаре, sorteddictionary 0 (это уже sorteddictionary )).
Показать ещё 1 комментарий
4

Предположим, что у нас есть словарь как

   Dictionary<int, int> dict = new Dictionary<int, int>();
   dict.Add(21,1041);
   dict.Add(213, 1021);
   dict.Add(45, 1081);
   dict.Add(54, 1091);
   dict.Add(3425, 1061);
   sict.Add(768, 1011);

1) вы можете использовать temporary dictionary to store values as:

        Dictionary<int, int> dctTemp = new Dictionary<int, int>();

        foreach (KeyValuePair<int, int> pair in dict.OrderBy(key => key.Value))
        {
            dctTemp .Add(pair.Key, pair.Value);
        }
-2

Учитывая, что у вас есть словарь, вы можете сортировать его непосредственно по значениям, используя ниже один лайнер:

var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
-3

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

Dictionary <<string, string>> ShareUserNewCopy = 
       ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
                                                        pair => pair.Value);                                          
  • 8
    Поместив отсортированные элементы обратно в словарь, они больше не гарантируют сортировку при перечислении нового словаря.
  • 2
    И почему вы добавляете этот ответ, когда на него уже есть ответ?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню