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





Осмотревшись и используя некоторые возможности C# 3.0, мы можем сделать это:
foreach (KeyValuePair<string,int> item in keywordCounts.OrderBy(key=> key.Value))
{
// do something with item.Key and item.Value
}
Это самый чистый способ, который я видел, и он похож на способ обработки хэшей в Ruby.
Не забудьте добавить пространство имен System.Linq при использовании этого синтаксиса.
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value) - небольшое дополнение к вашему ответу :) Спасибо, кстати :)
@ AndriusNaruševičius: Если вы добавите полученные элементы обратно в словарь, вы уничтожите порядок, как заказ словарей не гарантируется..
На высоком уровне у вас нет другого выбора, кроме как просмотреть весь Словарь и просмотреть каждое значение.
Может это поможет: 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);
}
);
строкаnextPair -> строка> nextPair строкаfirstPair -> строка> firstPair
Идеальное решение, отличное от Linq. Меня не перестает удивлять, как люди чувствуют необходимость использовать Linq, даже когда это абсолютно не требуется для решения проблемы. Я считаю, что с помощью C# 3 вы также можете упростить сортировку, просто используя лямбда: myList.Sort ((x, y) => x.Value.CompareTo (y.Value));
Использовать:
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));
Я использовал это решение (спасибо!), Но был сбит с толку на минуту, пока не прочитал сообщение Майкла Стума (и его фрагмент кода от Джона Тимни) и не понял, что myList - это вторичный объект, список KeyValuePairs, который создается из словаря, а потом отсортировал.
извините, но этот ответ было трудно понять, поскольку я не знаком с ключевым словом делегата (возможно, отличается в vb), n неясно, где происходит сортировка, поскольку вам нужно либо запустить количество элементов, умноженное на количество элементов (без элементов в квадрате) сравнения путем поиска / сравнения каждого элемента со всем словарем для каждого элемента, или если вы просто сравниваете текущий n последний, вам нужно будет сделать это в более чем одном цикле по коллекции, что вот почему я не получил его как есть. может быть, было бы полезно узнать больше о том, где происходит сортировка и переупорядочивание!
это одна подкладка - подтяжки не нужны. его можно переписать как myList.Sort((x,y)=>x.Value.CompareTo(y.Value));
Как бы этот оператор делегата в методе сортировки выглядел в VB.NET?
Я знаю, что это два года спустя ... но я уверен, что это могло бы помочь кому-то вместо предыдущего комментария: myList.Sort (Function (firstPair As KeyValuePair (Of String, String), nextPair As KeyValuePair (Of String, String) )) firstPair.Value.CompareTo (nextPair.Value))
Для сортировки по убыванию переключите x и y при сравнении: myList.Sort ((x, y) => y.Value.CompareTo (x.Value));
Я думаю, стоит отметить, что для этого требуется Linq для метода расширения ToList.
Вы, ребята, слишком усложняете это - словарь уже реализует IEnumerable, так что вы можете получить отсортированный список вроде этого: var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
Я обнаружил, что ToList () не нужен. Вы можете создать список с другой коллекцией, поэтому: var l = new List
Сортировка на месте
Используйте 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.
Как я могу изменить sortedDict обратно в Dictionary
К сожалению, это не работает на VS2005 из-за наличия .net framework 2.0 (без LINQ). Хорошо, если есть ответ Бамбрика.
Отличный ответ. И, как упоминалось в других комментариях к другим ответам, обязательно включите «using System.Linq;» вверху файла. В противном случае вы получите несколько запутанные сообщения об ошибках, и IntelliSense не поможет.
Я не уверен, всегда ли это работает, потому что повторение словаря не гарантирует, что KeyValuePairs будут «вытянуты» в том же порядке, в котором они были вставлены. Следовательно, не имеет значения, используете ли вы orderby в LINQ, потому что Dictionary может изменять порядок вставляемых элементов. Обычно это работает так, как ожидалось, но НЕТ ГАРАНТИИ, особенно для больших словарей.
Тип возврата должен быть IEnumerable<KeyValuePair<TKey, TValue>> или OrderedDictionary<TKey, TValue>. Или следует использовать SortedDictionary с самого начала. Для простого Dictionary в MSDN четко указано: «Порядок, в котором возвращаются элементы, не определен». Похоже, виновата последняя редакция @ rythos42. :)
Не обращайте внимания на все предложения .ToDictionary - стандартные словари не гарантируют порядок сортировки
Я просто использовал простой метод, чтобы сопоставить это с другим ответом с самым высоким рейтингом (с использованием Stopwatch()), и получил почти увеличение времени 350% с использованием LINQ. Список из четырех (!) Участников отсортирован другим методом = 0,0039511 секунды; (тот же) список из четырех элементов, отсортированный с использованием метода LINQ = 0,0130195 секунды.
@BorisB. Откатился на правильную версию. Вы тоже могли бы это сделать. Кто угодно может редактировать, чтобы сделать ТАК лучше. Для всех ToDictionary не является правильным ответом на эту проблему, и в исходном ответе caryden его не было. Удачного голосования за и против.
Для тех, кто использует .NET 2.0 - пробовали ли вы использовать LINQBridge, чтобы заполнить пробел? albahari.com/nutshell/linqbridge.aspx
@AlexG Тогда как мы можем преобразовать этот sortedDict обратно в Dictionary
Вызов '.ToDictionary (pair => pair.Key, pair => pair.Value); 'приводит к неупорядоченному словарю. Как показывают другие комментарии, это неверно.
Мне нравится окончательная форма этого ответа за гибкость, которую он предлагает. Например, если вам нужен просто список переупорядоченных ключей, вы просто измените select на: select entry.
В любом случае вы никогда не сможете отсортировать словарь. На самом деле они не заказываются. Гарантия для словаря состоит в том, что коллекции ключей и значений являются итеративными, а значения могут быть извлечены по индексу или ключу, но нет никакой гарантии какого-либо конкретного порядка. Следовательно, вам нужно будет добавить пару «имя-значение» в список.
Однако отсортированный словарь может дать список пар ключ-значение.
@recursive Любой словарь должен дать это. Интересно отметить, что мой ответ, который является правильным, но неполным (мог бы сделать то, что сделали лучшие примеры), проголосован ниже недопустимого ответа, который приведет к исключениям для повторяющихся значений в исходном словаре (ключи уникальны, значения не гарантируются быть)
Это лучший ответ, потому что Dictionary не сортируется. Он хеширует ключи, и вы можете выполнять по нему очень быстрый поиск.
@NetMage Да. Но другая часть проблемы заключается в том, что они хотели упорядочить по Value. И вы могли сделать это, только поменяв местами ключ и значение. И Value не обязательно уникально, но Key должен быть.
Да, но я думаю, что ваш ответ неверен из-за абсолютных утверждений в нем.
@NetMage И OP заявил, что SortedDictionary не подходит.
Самый простой способ получить отсортированный словарь - использовать встроенный класс 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
Как вы упомянули в своем комментарии, SortedDictionary сортирует по ключам. OP хочет отсортировать по значению. SortedDictionary в этом случае не помогает.
Что ж ... Если он / она (вы) может, просто установите значения в качестве ключей. Я рассчитал время операций, и sorteddictionary() всегда выигрывал по крайней мере на 1 микросекунду, и им намного проще управлять (поскольку накладные расходы на преобразование его обратно во что-то легко взаимодействующее и управляемое аналогично словарю, равно 0 (это уже sorteddictionary)) .
@mbrownnyc - нет, для этого требуется предположение или предварительное условие, что ЗНАЧЕНИЯ уникальны, что не гарантируется.
Сортировка списка 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>
Или для удовольствия вы можете использовать некоторые преимущества расширения 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));
var ordered = dict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
Это хорошее решение, но оно должно быть прямо перед конечной точкой с запятой: .ToDictionary (pair => pair.Key, pair => pair.Value);
Я предпочитаю этот, чистый и простой. @Gravitas: Я согласен, и версия фреймворка не упоминалась в OP.
Потому что это требует возврата к словарю, что не всегда прямолинейно ...
@theJerm, вернув отсортированные элементы обратно в словарь, будет ли тогда гарантирован порядок? Это может сработать сегодня, но это не гарантировано.
Используя фреймворк 4.5, просто проверил, что нет требует возврата к словарю.
Не должно быть возврата к словарю, потому что словари не упорядочены. Нет никакой гарантии, что KeyValuePairs останется в том порядке, в котором вы хотите.
Вы можете отсортировать словарь по значению и сохранить его обратно в себя (чтобы при переходе через foreach значения выводились по порядку):
dict = dict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
Конечно, это может быть неверно, но это работает.
Вы также можете использовать OrderByDescending, если хотите отсортировать по убыванию списка.
У меня сработало, хотя пришлось немного изменить на: Dictionary
Эта «работа» не гарантируется. Это деталь реализации. В других случаях это не должно работать. Неверный ответ, проголосовали против.
НЕ гарантируется, что вывод словаря будет иметь какой-либо конкретный порядок сортировки.
Я был бы очень обеспокоен, увидев это в производственном коде. Это не гарантируется и может измениться в любой момент. Не то чтобы я уклонялся от прагматических решений, это просто показывает непонимание структуры данных imo.
Я думаю, что это БУДЕТ работать, если элементы будут добавлены в словарь одновременно и ничего не будет удалено / изменено. есть ли доказательства того, что .NET будет переупорядочивать элементы в словаре?
var temp_enumerated_once_never_modified = dict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value); Там его починили и работает.
Вы можете отсортировать словарь по значению и получить результат в словаре, используя приведенный ниже код:
Dictionary <<string, string>> ShareUserNewCopy =
ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
pair => pair.Value);
Если поместить отсортированные элементы обратно в словарь, они больше не будут гарантированно отсортированы при перечислении нового словаря.
И зачем вы добавляете этот ответ, когда на него уже ответили?
Вы не сортируете записи в Словаре. Класс словаря в .NET реализован в виде хеш-таблицы - эта структура данных не подлежит сортировке по определению.
Если вам нужно иметь возможность перебирать вашу коллекцию (по ключу) - вам нужно использовать SortedDictionary, который реализован как двоичное дерево поиска.
Однако в вашем случае исходная структура не имеет значения, потому что она отсортирована по другому полю. Вам все равно нужно будет отсортировать его по частоте и поместить в новую коллекцию, отсортированную по соответствующему полю (частоте). Итак, в этой коллекции частоты являются ключами, а слова - значениями. Поскольку многие слова могут иметь одинаковую частоту (и вы собираетесь использовать их в качестве ключа), вы не можете использовать ни Dictionary, ни SortedDictionary (для них требуются уникальные ключи). Это оставляет вас с SortedList.
Я не понимаю, почему вы настаиваете на сохранении ссылки на исходный элемент в своем основном / первом словаре.
Если объекты в вашей коллекции имеют более сложную структуру (больше полей), и вам нужно иметь возможность эффективно получать доступ / сортировать их, используя несколько разных полей в качестве ключей - вам, вероятно, понадобится настраиваемая структура данных, которая будет состоять из основного хранилища, которое поддерживает вставку и удаление O (1) (LinkedList) и несколько структур индексации - Dictionaries / 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);
}
Учитывая, что у вас есть словарь, вы можете отсортировать их непосредственно по значениям, используя один лайнер ниже:
var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
Этот ответ неверен, так как заказ полученного словаря не гарантируется.
Предположим, у нас есть словарь как
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);
}
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);
Другие ответы хороши, если все, что вам нужно, это иметь «временный» список, отсортированный по значению. Однако, если вы хотите, чтобы словарь, отсортированный по 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> и BMultiMap<Value,Key>.
Подобно http://stackoverflow.com/questions/268321, но может заменить каждый словарь на SortedDictionary. Хотя ответы, похоже, не поддерживают повторяющиеся значения (предполагается от 1 до 1).
На самом деле в C# словари нет методов sort ().
Поскольку вас больше интересует сортировка по значениям,
вы не можете получить значения, пока не предоставите им ключ.
Короче говоря, вам нужно перебрать их, используя LINQ OrderBy(),
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, определяемый пользователем класс).
Если он одиночный, вы можете составить его список, а затем применить sort.
Если это определяемый пользователем класс, то этот класс должен реализовывать IComparable, ClassName: IComparable<ClassName> и переопределять compareTo(ClassName c), поскольку они более быстрые и объектно-ориентированные, чем LINQ.
Требуемое пространство имен: using System.Linq;
Dictionary<string, int> counts = new Dictionary<string, int>();
counts.Add("one", 1);
counts.Add("four", 4);
counts.Add("two", 2);
counts.Add("three", 3);
Сортировать по убыванию:
foreach (KeyValuePair<string, int> kvp in counts.OrderByDescending(key => key.Value))
{
// some processing logic for each item if you want.
}
Заказ по возрастанию:
foreach (KeyValuePair<string, int> kvp in counts.OrderBy(key => key.Value))
{
// some processing logic for each item if you want.
}
Сортировка и печать:
var items = from pair in players_Dic
orderby pair.Value descending
select pair;
// Display results.
foreach (KeyValuePair<string, int> pair in items)
{
Debug.Log(pair.Key + " - " + pair.Value);
}
Измените нисходящий на восходящий, чтобы изменить порядок сортировки
Помимо простой сортировки словаря (как в принятом ответе), вы также можете просто создать
IComparer, который выполняет трюк (правда, он принимает ключ для сравнения, но с помощью ключа вы можете получить значение). ;-)