Мне часто приходится сортировать словарь, состоящий из ключей и значений, по значению. Например, у меня есть хэш слов и соответствующих частот, которые я хочу упорядочить по частоте.
Существует вариант, SortedList
который подходит для одного значения (скажем, частоты), и я хочу сопоставить его со словом.
SortedDictionary заказывает по ключу, а не по значению. Некоторые прибегают к пользовательскому классу , но есть ли более чистый способ?
Использование:
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.Sort((x,y)=>x.Value.CompareTo(y.Value));
IEnumerable
, так что вы можете получить отсортированный список, например, такой:var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
Используйте 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
предложение.
IEnumerable<KeyValuePair<TKey, TValue>>
или OrderedDictionary<TKey, TValue>
. Или следует использовать SortedDictionary
с самого начала. Для простого Dictionary
MSDN ясно заявляет, что "порядок, в котором возвращаются элементы, не определен". Похоже, что последняя редакция @ rythos42 виновата. :)var ordered = dict.OrderBy(x => x.Value);
Оглядываясь вокруг, и используя некоторые функции C # 3.0, мы можем сделать это:
foreach (KeyValuePair<string,int> item in keywordCounts.OrderBy(key=> key.Value))
{
// do something with item.Key and item.Value
}
Это самый чистый способ, который я видел, и он похож на способ обработки хэшей в Ruby.
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)
- просто небольшое дополнение к вашему ответу :) Спасибо, кстати :)Вы можете отсортировать словарь по значению и сохранить его обратно в себе (чтобы при переходе к нему значения выходили по порядку):
dict = dict.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
Конечно, это может быть не правильно, но это работает.
На высоком уровне у вас нет другого выбора, кроме как пройти весь словарь и посмотреть на каждое значение.
Может быть, это поможет: 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);
}
);
Вы никогда не сможете отсортировать словарь в любом случае. Они на самом деле не заказаны. Гарантии для словаря состоят в том, что наборы ключей и значений являются итеративными, а значения могут быть получены по индексу или ключу, но нет гарантии какого-либо конкретного порядка. Следовательно, вам нужно получить пару имя-значение в список.
Вы не сортируете записи в Словаре. Класс словаря в .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);
}
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);
Или для удовольствия вы можете использовать некоторые расширения 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));
Сортировка 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>
Самый простой способ получить отсортированный словарь - использовать встроенный 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
сортировка по ключам. ОП хочет отсортировать по значению. SortedDictionary
не помогает в этом случае. sorteddictionary()
всегда выигрывал, по крайней мере, на 1 микросекунду, и им намного легче управлять (поскольку затраты на преобразование его обратно во что-то, с чем легко взаимодействовать и управлять им, подобно словарю, равны 0 (это уже a sorteddictionary
)). Другие ответы хороши, если все, что вам нужно, это иметь «временный» список, отсортированный по значению. Однако, если вы хотите, чтобы словарь, отсортированный по нему 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>
.
Предположим, у нас есть словарь
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);
}
На самом деле в 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, и более объектно-ориентированы.
Вы можете отсортировать словарь по значению и получить результат в словаре, используя код ниже:
Dictionary <<string, string>> ShareUserNewCopy =
ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
pair => pair.Value);
Учитывая, что у вас есть словарь, вы можете отсортировать их непосредственно по значениям, используя ниже одну строку:
var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.Value);
IComparer
которая делает трюк (правда, она принимает ключ для сравнения, но с помощью ключа вы можете получить значение). ;-)