您如何按值对字典排序?


796

我经常必须按值对由键和值组成的字典进行排序。例如,我有一个单词和各个频率的哈希,我想按频率排序。

SortedList我想将其映射回单词,这对于单个值(例如频率)来说是一个很好的选择。

SortedDictionary订单按键而非值排序。有些使用自定义类,但是有没有更干净的方法?


1
除了仅对字典进行排序(如在接受的答案中一样)外,您还可以创建一个IComparer完成窍门的方法(确实它接受要比较的键,但有了键,就可以得到一个值)。;-)
BrainSlugs83

Answers:


520

采用:

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(或更高版本)的编译器时,才能使用此语法。

var myList = aDictionary.ToList();

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

26
我使用了这个解决方案(谢谢!),但直到我读了Michael Stum的帖子(以及John Timney的他的代码片段),并困惑了一分钟,才意识到myList是第二个对象,一个KeyValuePairs列表,它是从字典中创建的,然后排序。
罗宾·贝内特

113
它是一支衬管-您不需要大括号。可以重写为myList.Sort((x,y)=>x.Value.CompareTo(y.Value));
Arnis Lapsa,2010年

25
要对降序进行排序,请在比较中切换x和y:myList.Sort((x,y)=> y.Value.CompareTo(x.Value));
阿图罗

8
我认为值得注意的是,这需要Linq作为ToList扩展方法。

17
你们对于将其复杂化IEnumerablevar mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
不屑一顾

523

使用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子句。


15
如何将sortedDict改回Dictionary <string,int>?发布新的SO问题在这里:stackoverflow.com/questions/3066182/...
Kache

1
可悲的是,由于.net framework 2.0在那里(没有LINQ),这在VS2005上不起作用。最好也有Bambrick的答案。
Smalcat

22
我不确定它是否总是有效,因为在字典上进行迭代并不能保证KeyValuePairs的插入顺序与它们插入时的顺序相同。如此,是否在LINQ中使用orderby并不重要,因为Dictionary可以更改插入元素的顺序。它通常可以按预期工作,但没有保证,特别是对于大型词典。
Bozydar Sobczak 2012年

16
返回类型应为IEnumerable<KeyValuePair<TKey, TValue>>OrderedDictionary<TKey, TValue>。或者应该SortedDictionary从一开始就使用a 。简而言之Dictionary,MSDN明确声明“退回商品的顺序未定义。”。似乎是@ rythos42的最新编辑。:)
Boris B.

16
请忽略所有建议.ToDictionary- 标准词典不保证排序顺序
AlexFoxGill 2013年

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

6
我不确定为什么这种解决方案不流行-也许是因为它需要.NET 3.5?
Contango 2011年

33
这是一个很好的解决方案,但是应该在分号结尾之前使用:.ToDictionary(pair => pair.Key,pair => pair.Value);
theJerm 2012年

3
@theJerm通过将排序后的项目放回字典中,可以保证顺序吗?它可能在今天有效,但不能保证。
2013年

1
使用4.5框架,只是证实,它并没有需要强制回字典。
Jagd 2014年

12
不应强制转换为字典,因为字典没有顺序。不能保证KeyValuePairs会保持您想要的顺序。
戴维·德玛

165

环顾四周,并使用一些C#3.0功能,我们可以这样做:

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

这是我所见过的最干净的方法,类似于Ruby处理哈希的方法。


在将KeyValuePairs添加到ComboBox的过程中,我试图对字典进行排序...效果很好!谢谢!
杰森·唐

6
使用此语法时,请不要忘记添加System.Linq命名空间。
M. Dudley 2010年

4
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)-只是您的回答的一小部分:)谢谢,顺便说一句:)
AndriusNaruševičius2012年

7
@AndriusNaruševičius:如果将结果项加回到字典中,则会破坏顺序,因为不能保证以任何特定方式对字典进行排序
OR Mapper,2015年

这很方便。怎么可以倒过来呢?
丹·黑斯廷斯

158

您可以按值对字典进行排序,然后将其保存回自己(这样,当您遍历字典时,值将按顺序显示):

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

当然,这可能不正确,但是可以。


8
如果要排序到降序列表,也可以使用OrderByDescending。
Mendokusai 2011年

为我工作,尽管我不得不将其稍微更改为:Dictionary <key,value> dict = dict.OrderBy(x => x.Value).ToDictionary(x => x.Key,x => x.Value);
乔什

17
这种“工作”是不能保证的。其实现细节。它不需要其他时间工作。错误的答案,被否决了。
nawfal 2014年

4
不保证字典输出具有任何特定的排序顺序。
罗杰·威尔考克斯

5
我很担心在生产代码中看到这一点。无法保证,并且随时可能更改。并不是说我回避了实用的解决方案,而是表明对imo的数据结构缺乏了解。
jamespconnor 2015年

61

在较高的层次上,您别无选择,只能遍历整个Dictionary并查看每个值。

也许有帮助:http : //bytes.com/forum/thread563638.html从约翰·蒂姆尼(John Timney)复制/粘贴:

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

3
stringnextPair - >字符串> nextPair stringfirstPair - >字符串> firstPair
艺术

2
完美的非Linq解决方案。即使绝对不需要解决问题,人们也总是觉得使用Linq的必要性使我感到惊奇。使用C#3,我相信您还可以简化排序以仅使用lambda:myList.Sort((x,y)=> x.Value.CompareTo(y.Value));

25

无论如何,您永远都无法对字典进行排序。它们实际上没有订购。字典的保证是键和值的集合是可迭代的,并且可以通过索引或键来检索值,但是不保证任何特定的顺序。因此,您需要将名称值对放入列表中。


2
经过排序的字典可能会产生一个键-值对列表。
递归

1
@recursive任何字典都应该产生该结果。有趣的是,我的答案是正确的,但不完整(本可以做更好的例子所做的),在无效答案下投票,这将导致原始词典中重复值的异常(键是唯一的,不能保证值)
罗杰·威尔考克斯

5
这是bes的答案,因为Dictionary无法排序。它对键进行哈希处理,您可以对其执行非常快速的查找操作。
Paulius Zaliaduonis

@NetMage是的。但是问题的另一部分是他们希望按价值排序。而且,您只能通过交换键和值来做到这一点。价值不一定是唯一的,但关键必须是唯一的。
罗杰·威尔科克斯

是的,但是我认为您的回答是错误的,因为其中包含绝对的说明。
NetMage

21

您不对字典中的条目进行排序。.NET中的字典类被实现为哈希表-此数据结构无法按定义排序。

如果您需要能够(按键)遍历集合-您需要使用SortedDictionary,它被实现为Binary Search Tree。

但是,在您的情况下,源结构无关紧要,因为它是按其他字段排序的。您仍然需要按频率对其进行排序,并将其放入按相关字段(频率)进行排序的新集合中。因此,在此集合中,频率是键,而单词是值。由于许多单词可以具有相同的频率(并且您将使用它作为键),因此您既不能使用Dictionary也不能使用SortedDictionary(它们需要唯一的键)。这给您留下了SortedList。

我不明白您为什么坚持在主/第一个词典中保持指向原始项目的链接。

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

您需要协调插入和删除操作,以使索引与主集合(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);
}

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

12

或者为了娱乐,您可以使用一些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));

10

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

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>

7

获取排序的Dictionary的最简单方法是使用内置的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
正如您在评论中提到的那样,SortedDictionary按键排序。OP希望按值排序。 SortedDictionary在这种情况下无济于事。
马蒂·尼尔

好吧...如果他/她(您)可以,只需将值设置为键即可。我对操作进行计时,并且sorteddictionary()总是赢得至少1微秒的时间,而且它管理起来要容易得多(因为将其转换回易于与词典交互和管理的东西的开销为0(它已经是sorteddictionary))。
mbrownnyc 2013年

2
@mbrownnyc-不,这样做需要假设或前提是VALUES是唯一的,但不能保证。
罗杰·威尔考克斯

6

如果您想要的是按“值”排序的“临时”列表,则其他答案也不错。但是,如果您希望字典排序依据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>


http://stackoverflow.com/questions/268321类似,但可以将每个Dictionary替换为SortedDictionary。虽然答案似乎不支持重复值(假设1到1)。
crokusek '16

3

假设我们有一个字典

   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

实际上,在C#中,Dictionary 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;

它也取决于您要存储哪种类型的值,
它是单个值(如字符串,int)还是多个(如List,Array,用户定义的类),
如果是单个值,则可以对其进行排序然后应用排序。
如果是用户定义的类,则该类必须实现IComparable,
ClassName: IComparable<ClassName>并进行覆盖,compareTo(ClassName c) 因为它们比LINQ更快,并且面向对象。


0

所需的名称空间: 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);

按desc排序:

foreach (KeyValuePair<string, int> kvp in counts.OrderByDescending(key => key.Value))
{
// some processing logic for each item if you want.
}

按Asc排序:

foreach (KeyValuePair<string, int> kvp in counts.OrderBy(key => key.Value))
{
// some processing logic for each item if you want.
}

-2

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

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

8
通过将排序后的项目放回词典中,当您枚举新词典时,不再保证可以对它们进行排序。
Marty Neal 2012年

2
为何您已经回答了这个问题又为什么要添加呢?
2013年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.