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

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

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

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

Ответов (19)

Решение

Использовать:

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

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

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

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

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

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

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

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

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

Другие ответы хороши, если все, что вам нужно, это иметь «временный» список, отсортированный по значению. Однако, если вы хотите, чтобы словарь, отсортированный по нему 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>.

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

var x = (from c in dict orderby c.Value.Order ascending select c).ToDictionary(c => c.Key, c=>c.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);

На самом деле в 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, определяемый пользователем класс). Если он один, вы можете составить его список, а затем применить сортировку.
Если это определяемый пользователем класс, то этот класс должен реализовать 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);
}

Измените нисходящий на восходящий, чтобы изменить порядок сортировки

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

Возможно, это поможет: 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);
    }
);

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

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

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

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

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