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

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

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

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

2.08.2008 00:40:58
Помимо простой сортировки словаря (как в принятом ответе), вы также можете просто создать функцию, IComparerкоторая делает трюк (правда, она принимает ключ для сравнения, но с помощью ключа вы можете получить значение). ;-)
BrainSlugs83 19.03.2018 21:16:38
17 ОТВЕТОВ
РЕШЕНИЕ

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

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));
517
31.08.2017 23:01:42
Я использовал это решение (спасибо!), Но был в замешательстве в течение минуты, пока не прочитал пост Майкла Стума (и его фрагмент кода от Джона Тимни) и понял, что myList - это вторичный объект, список KeyValuePairs, который создается из словаря, а потом отсортировано.
Robin Bennett 31.03.2009 13:33:19
это один лайнер - вам не нужны брекеты. это может быть переписано какmyList.Sort((x,y)=>x.Value.CompareTo(y.Value));
Arnis Lapsa 26.09.2010 16:40:27
Для сортировки по убыванию переключите x и y на сравнение: myList.Sort ((x, y) => y.Value.CompareTo (x.Value));
Arturo 16.10.2012 22:43:48
Я думаю, что стоит отметить, что для этого требуется Linq для метода расширения ToList.
Ben 15.10.2014 23:41:18
Вы, ребята, недоумеваете, усложнив это - словарь уже реализован IEnumerable, так что вы можете получить отсортированный список, например, такой:var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
BrainSlugs83 19.03.2018 21:12:45

Используйте 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предложение.

520
31.08.2017 23:02:53
Как я могу изменить sortedDict обратно в словарь <string, int>? Выложен новый ТАК вопрос здесь: stackoverflow.com/questions/3066182/…
Kache 17.06.2010 22:47:52
К сожалению, это не работает на VS2005 из-за .net Framework 2.0 там (без LINQ). Хорошо также иметь ответ Бамбрика.
Smalcat 30.11.2010 11:23:19
Я не уверен, всегда ли это работает, потому что перебор словаря не гарантирует, что KeyValuePairs «вытянуты» в том же порядке, в котором они были вставлены. Поэтому не имеет значения, используете ли вы orderby в LINQ, поскольку Dictionary может изменить порядок вставленных элементов. Обычно он работает как положено, но ГАРАНТИИ НЕТ, особенно для больших словарей.
Bozydar Sobczak 27.01.2012 08:23:53
Тип возврата должен быть IEnumerable<KeyValuePair<TKey, TValue>>или OrderedDictionary<TKey, TValue>. Или следует использовать SortedDictionaryс самого начала. Для простого DictionaryMSDN ясно заявляет, что "порядок, в котором возвращаются элементы, не определен". Похоже, что последняя редакция @ rythos42 виновата. :)
Boris B. 7.02.2012 20:05:45
Пожалуйста, не .ToDictionary
AlexFoxGill 15.03.2013 16:57:22
var ordered = dict.OrderBy(x => x.Value);
250
11.11.2010 17:16:38
Я не уверен, почему это решение не более популярно - возможно, потому что для него требуется .NET 3.5?
Contango 16.06.2011 08:48:49
Это хорошее решение, но оно должно иметь это право перед конечной точкой с запятой: .ToDictionary (pair => pair.Key, pair => pair.Value);
theJerm 30.03.2012 17:55:00
@theJerm, поместив отсортированные элементы обратно в словарь, тогда порядок гарантирован? Это может работать сегодня, но это не гарантировано.
nawfal 31.10.2013 07:41:36
Используя платформу 4.5, только что убедился, что она не требует приведения обратно в словарь.
Jagd 26.06.2014 19:36:09
Не должно быть преобразования в словарь, потому что словари не упорядочены. Нет гарантии, что KeyValuePairs останется в том порядке, в котором вы хотите.
David DeMar 19.11.2014 14:54:10

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

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

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

164
29.01.2013 17:11:28
Я пытался отсортировать словарь при добавлении KeyValuePairs в ComboBox ... это работало отлично! Спасибо!
Jason Down 17.03.2009 18:19:24
Не забудьте добавить пространство имен System.Linq при использовании этого синтаксиса.
M. Dudley 7.06.2010 15:10:11
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)- просто небольшое дополнение к вашему ответу :) Спасибо, кстати :)
Andrius Naruševičius 21.09.2012 07:17:03
@ AndriusNaruševičius: Если вы добавите полученные элементы обратно в словарь, вы уничтожите порядок, так как словари не гарантированно заказаны каким-либо конкретным способом .
O. R. Mapper 24.01.2015 10:25:04
Это было удобно. Как это можно перевернуть, чтобы пойти другим путем?
Dan Hastings 17.01.2017 10:33:06

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

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

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

157
30.04.2015 09:29:19
Вы также можете использовать OrderByDescending, если хотите отсортировать по убыванию.
Mendokusai 17.08.2011 02:16:05
Работал для меня, хотя мне пришлось немного изменить его на: Словарь <ключ, значение> dict = dict.OrderBy (x => x.Value) .ToDictionary (x => x.Key, x => x.Value);
Josh 15.12.2011 09:34:28
Это «работа» не гарантируется. Это деталь реализации. Это не должно работать в другое время. Неправильный ответ.
nawfal 30.04.2014 13:45:55
Выходной словарь НЕ гарантированно имеет какой-либо определенный порядок сортировки.
Roger Willcocks 24.03.2015 02:12:29
Я был бы весьма обеспокоен, увидев это в рабочем коде. Это не гарантируется и может измениться в любое время. Не то чтобы я уклонялся от прагматичных решений, это просто показывает отсутствие понимания структуры данных imo.
jamespconnor 28.04.2015 13:24:18

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

Может быть, это поможет: 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);
    }
);
61
17.10.2019 07:24:37
stringnextPair -> строка> nextPair stringfirstPair -> строка> firstPair
Art 25.02.2010 23:38:12
Идеальное решение, отличное от Linq. Меня не перестает удивлять то, как люди чувствуют необходимость использовать Linq, даже когда это абсолютно не требуется для решения проблемы. Я полагаю, что в C # 3 вы также можете упростить сортировку, просто используя лямбду: myList.Sort ((x, y) => x.Value.CompareTo (y.Value));
user502255 6.07.2016 01:28:23

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

25
15.05.2019 02:00:23
Сортированный словарь может дать список пар ключ-значение.
recursive 20.12.2008 05:19:53
@recursive Любой словарь должен дать это. Интересно отметить, что мой ответ, который является правильным, но неполным (мог бы сделать то, что сделали лучшие примеры), голосуется ниже недопустимого ответа, что приведет к исключениям для дублирующихся значений в исходном словаре (ключи уникальны, значения не гарантируются быть)
Roger Willcocks 7.07.2010 05:30:06
Это лучший ответ, потому что словарь не сортируется. Он хэширует ключи, и вы можете выполнить на нем чрезвычайно быструю операцию поиска.
Paulius Zaliaduonis 24.08.2011 09:09:28
@NetMage Да. Но другая часть проблемы заключается в том, что они хотели упорядочить по стоимости. И вы могли бы сделать это только путем замены ключа и значения. И значение не обязательно уникально, но ключ должен быть.
Roger Willcocks 29.09.2019 02:32:27
Да, но я думаю, что ваш ответ неверен из-за абсолютных утверждений в нем.
NetMage 30.09.2019 19:45:50

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

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

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

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

Если объекты в вашей коллекции имеют более сложную структуру (больше полей) и вам необходимо иметь возможность эффективно обращаться к ним / сортировать их, используя несколько различных полей в качестве ключей - вам, вероятно, понадобится настраиваемая структура данных, которая будет состоять из основного хранилища, которое поддерживает вставку и удаление O (1) (LinkedList) и несколько структур индексирования - Словари / SortedDictionaries / SortedLists. Эти индексы будут использовать одно из полей вашего сложного класса в качестве ключа и указатель / ссылку на 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);
}
21
31.10.2013 07:43:51
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);
15
20.07.2015 11:01:19

Или для удовольствия вы можете использовать некоторые расширения 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));
12
30.06.2010 11:12:21

Сортировка 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>
10
31.08.2017 23:04:12

Самый простой способ получить отсортированный словарь - использовать встроенный 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

7
5.01.2012 09:25:22
Как вы упоминаете в своем комментарии, SortedDictionaryсортировка по ключам. ОП хочет отсортировать по значению. SortedDictionaryне помогает в этом случае.
Marty Neal 12.09.2012 15:50:12
Хорошо ... Если он / она (вы) можете, просто установите значения в качестве ключей. Я рассчитывал время выполнения операций и sorteddictionary()всегда выигрывал, по крайней мере, на 1 микросекунду, и им намного легче управлять (поскольку затраты на преобразование его обратно во что-то, с чем легко взаимодействовать и управлять им, подобно словарю, равны 0 (это уже a sorteddictionary)).
mbrownnyc 25.10.2013 14:17:44
@mbrownnyc - нет, для этого требуется допущение или предварительное условие, что ЗНАЧЕНИЯ уникальны, что не гарантируется.
Roger Willcocks 24.03.2015 02:14:59

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

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

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

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

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

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

6
16.10.2018 15:51:28
Аналогично http://stackoverflow.com/questions/268321, но может заменить каждый словарь на SortedDictionary. Хотя ответы выглядят не для поддержки повторяющихся значений (предполагается от 1 до 1).
crokusek 11.07.2016 23:35:55

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

   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);
        }
3
2.02.2015 11:21:58

На самом деле в C # словари dint имеют методы sort (), так как вас больше интересует сортировка по значениям, вы не можете получать значения до тех пор, пока не предоставите их ключ, короче говоря, вам нужно перебирать их, используя LINQ Order By,

var items = new Dictionary<string, int>();
items.Add("cat", 0);
items.Add("dog", 20);
items.Add("bear", 100);
items.Add("lion", 50);

// Call OrderBy method here on each item and provide them the ids.
foreach (var item in items.OrderBy(k => k.Key))
{
    Console.WriteLine(item);// items are in sorted order
}

ты можешь сделать один трюк,

var sortedDictByOrder = items.OrderBy(v => v.Value);

или

var sortedKeys = from pair in dictName
            orderby pair.Value ascending
            select pair;

Он также зависит от того, какие значения вы храните
: одиночные (например, string, int) или множественные (например, List, Array, пользовательский класс),
если вы можете создать один из них, то примените сортировку.
если пользовательский класс, то этот класс должен реализовывать IComparable
ClassName: IComparable<ClassName>и переопределять, так compareTo(ClassName c) как они быстрее, чем LINQ, и более объектно-ориентированы.

3
26.02.2019 12:45:35

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

Dictionary <<string, string>> ShareUserNewCopy = 
       ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
                                                        pair => pair.Value);                                          
-2
24.07.2012 12:30:47
Поместив отсортированные элементы обратно в словарь, они больше не гарантируют сортировку при перечислении нового словаря.
Marty Neal 12.09.2012 15:48:28
И почему вы добавляете этот ответ, когда на него уже есть ответ?
nawfal 31.10.2013 07:42:52

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

var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
-2
31.05.2014 22:30:00
O. R. Mapper 24.01.2015 10:22:42