关于C#:如何按值对字典排序?

How do you sort a dictionary by value?

我经常要按值对字典进行排序,字典由键和值组成。例如,我有一个散列的单词和各自的频率,我想按频率排序。

有一个SortedList,它对单个值(比如说频率)很好,我想把它映射回单词。

SortedDictionary Orders by Key,而不是Value。有没有更干净的方法?


使用LINQ:

1
2
3
4
5
6
7
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子句。


用途:

1
2
3
4
5
6
7
8
9
10
11
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或更高版本,所以您可以将其简化为lambda语法——它是等效的,但更短。如果您的目标是.NET 2.0,则只有在使用Visual Studio 2008(或更高版本)中的编译器时,才能使用此语法。

1
2
3
var myList = aDictionary.ToList();

myList.Sort((pair1,pair2) => pair1.Value.CompareTo(pair2.Value));


1
var ordered = dict.OrderBy(x => x.Value);


环顾四周,使用一些C 3.0功能,我们可以做到:

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

这是我见过的最干净的方法,类似于Ruby处理散列的方法。


您可以按值对字典进行排序,并将其保存回自身(这样,当您对字典进行预测时,值会按顺序出现):

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

当然,它可能不正确,但它起作用。


在较高的层次上,您没有其他的选择,然后遍历整个字典并查看每个值。

也许这有助于:http://bytes.com/forum/thread563638.html从John Timney复制/粘贴:

1
2
3
4
5
6
7
8
9
10
11
12
13
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,它是作为二进制搜索树实现的。

然而,在您的情况下,源结构是不相关的,因为它是由不同的字段排序的。您仍然需要按频率对其进行排序,并将其放入按相关字段(频率)排序的新集合中。所以在这个集合中,频率是键,单词是值。因为许多单词可以有相同的频率(您将把它用作键),所以既不能使用字典,也不能使用排序字典(它们需要唯一的键)。这会给你一个排序列表。

我不明白你为什么坚持要在你的主/第一本字典中保持与原始项目的链接。

如果集合中的对象具有更复杂的结构(更多字段),并且需要使用多个不同的字段作为键来高效地访问/排序它们,则可能需要一个自定义数据结构,该结构由支持O(1)插入和删除(LinkedList)的主存储和多个索引结构组成。-字典/排序字典/排序列表。这些索引将使用复杂类中的一个字段作为键,并使用指向LinkedList中LinkedListNode的指针/引用作为值。

您需要协调插入和删除,以使索引与主集合(LinkedList)保持同步,我认为删除会非常昂贵。这类似于数据库索引的工作方式——它们对于查找非常有用,但当需要执行许多插入和删除操作时,它们会成为一种负担。

以上所有这些都是正当的,如果你要做一些查找繁重的处理。如果您只需要按频率排序后输出它们,那么您可以只生成一个(匿名)元组列表:

1
2
3
4
5
6
7
8
9
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);
}

1
2
3
Dictionary<string, string> dic= new Dictionary<string, string>();
var ordered = dic.OrderBy(x => x.Value);
return ordered.ToDictionary(t => t.Key, t => t.Value);


排序值

这将显示如何对字典中的值进行排序。我们看到一个控制台程序,您可以在Visual Studio中编译并运行。它向字典中添加键,然后按它们的值对它们进行排序。记住,字典实例最初不会以任何方式进行排序。我们在查询语句中使用linq orderby关键字。

订货条款对字典进行排序的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;
    }
}

产量

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

或者为了好玩,你可以使用一些Linq扩展功能:

1
2
3
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));


使用vb.net对SortedDictionary列表进行排序以绑定到ListView控件中:

1
2
3
4
5
6
7
8
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:

1
2
3
4
5
6
7
8
<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类:

1
2
3
4
5
6
//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的分类版本。


如果你只想有一个按值排序的"临时"列表,那么其他的答案是好的。但是,如果您希望一个按Key排序的字典自动与另一个按Value排序的字典同步,则可以使用Bijection类。

Bijection允许您使用两个现有字典初始化集合,因此如果您希望其中一个字典未排序,而另一个字典要排序,则可以使用类似

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

你可以像任何普通字典一样使用dict(它实现IDictionary),然后调用dict.Inverse得到由Value排序的"逆"字典。

Bijection是loyc.collections.dll的一部分,但如果您愿意,可以简单地将源代码复制到您自己的项目中。

注:如果有多个相同值的密钥,则不能使用Bijection,但可以在普通DictionaryBMultiMap之间手动同步。


假设我们有一本字典

1
2
3
4
5
6
7
   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

1
2
3
4
5
6
        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中,字典数据有sort()方法,由于您对按值排序更感兴趣,在提供值键之前无法获取值,简而言之,您需要迭代它们,使用Linq的order by,

1
2
3
4
5
6
7
8
9
10
11
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
}

你可以做一个技巧,

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

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

它还取决于您存储的是哪种值,它是单个的(如字符串、int)还是多个的(如列表、数组、用户定义的类),如果是单身,你可以列一张单子,然后应用排序。如果用户定义的类,那么该类必须实现IComparable,ClassName: IComparable和override compareTo(ClassName c)。因为它们比LINQ更快,而且更面向对象。


您可以按值对字典进行排序,并使用下面的代码在字典中获取结果:

1
2
3
Dictionary <<string, string>> ShareUserNewCopy =
       ShareUserCopy.OrderBy(x => x.Value).ToDictionary(pair => pair.Key,
                                                        pair => pair.Value);


如果您有字典,您可以使用下面的一行程序直接对其值进行排序:

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