辞書を値でどのようにソートしますか?


796

キーと値で構成される辞書を値でソートしなければならないことがよくあります。たとえば、単語とそれぞれの頻度のハッシュがあり、頻度で並べ替えます。

SortedList単一の値(頻度など)に適したものがあり、それを単語にマッピングします。

SortedDictionaryは、値ではなくキーで並べ替えます。カスタムクラスに頼る人もいますが、よりクリーンな方法はありますか?


1
(受け入れられた回答のように)辞書を並べ替えるだけでなくIComparer、トリックを実行するを作成することもできます(比較するためにキーを受け入れますが、キーを使用すると、値を取得できます)。;-)
BrainSlugs83 2018年

回答:


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以上を対象としているため、これをラムダ構文に単純化できます。これは同等ですが、より短い構文です。.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がセカンダリオブジェクトである、辞書から作成されたKeyValuePairのリストであることに気づくまで、しばらく混乱しました。そしてソートされます。
ロビンベネット

113
それは1つのライナーです-あなたはブレースを必要としません。次のように書き換えることができますmyList.Sort((x,y)=>x.Value.CompareTo(y.Value));
Arnis Lapsa

25
降順で並べ替えるには、比較でxとyを切り替えます。myList.Sort((x、y)=> y.Value.CompareTo(x.Value));
Arturo

8
これには、ToList拡張メソッドにLinqが必要であることは注目に値します。
ベン

17
皆さんはこれを複雑にしていることを気にしています-辞書はすでに実装されているIEnumerableので、次のようなソートされたリストを取得できます:var mySortedList = myDictionary.OrderBy(d => d.Value).ToList();
BrainSlugs83

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-aheadStartsWith句も含めることができます。


15
sortDictをDictionary <string、int>に戻すにはどうすればよいですか?:ここに新しいSOの質問投稿stackoverflow.com/questions/3066182/...
Kache

1
残念ながら、.NET Framework 2.0があるため(LINQがないため)、これはVS2005では機能しません。バンブリックの答えもあるといいですね。
Smalcat 2010年

22
辞書を繰り返し処理しても、KeyValuePairが挿入されたのと同じ順序で「プル」されることが保証されないため、常に機能するかどうかはわかりません。エルゴ、ディクショナリは挿入された要素の順序を変更できるため、LINQでorderbyを使用しても問題ありません。通常は期待どおりに動作しますが、特に大規模な辞書の場合、保証はありません。
Bozydar Sobczak 2012

16
戻り値の型は、あるべきIEnumerable<KeyValuePair<TKey, TValue>>OrderedDictionary<TKey, TValue>。またはSortedDictionary、最初からaを使用する必要があります。平野のためにDictionaryMSDNを明確に「の項目が返される順序が定義されていません。」述べています。@ rythos42の最新の編集に責任があるようです。:)
ボリスB.

16
すべての提案を無視してください.ToDictionary- 標準辞書はソート順を保証するものではありません
AlexFoxGill

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

6
.NET 3.5が必要なためか、なぜこのソリューションがそれほど普及していないのかわかりません。
Contango

33
これは良い解決策ですが、最後のセミコロンの直前に次のようにしてください。
theJerm

3
@theJermは、ソートされたアイテムを辞書に戻すことによって、その順序が保証されますか?今日は機能するかもしれませんが、保証はされていません。
nawfal 2013年

1
4.5フレームワークを使用して、ちょうどそれがないことを確認していない辞書にキャストバックを必要とします。
Jagd、2014年

12
辞書は順序付けられていないため、辞書へのキャストバックはありません。KeyValuePairsが希望する順序に留まる保証はありません。
David DeMar 2014年

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に追加するときに辞書を並べ替えようとしていましたが、これはうまくいきました!ありがとう!
Jason Down

6
この構文を使用するときは、忘れずにSystem.Linq名前空間を追加してください。
M.ダドリー

4
(for KeyValuePair<string, int> item in keywordCounts.OrderBy(key => key.Value) select item).ToDictionary(t => t.Key, t => t.Value)-あなたの答え
にほんの

7
@AndriusNaruševičius:結果のアイテムをディクショナリに追加すると、ディクショナリが特定の方法で注文されることが保証されないため、注文が破棄されます
またはMapper

これは便利でした。それを逆にして逆にするにはどうすればよいですか?
Dan Hastings

158

辞書を値でソートし、それ自体に保存することができます(そのため、それをforeachすると、値が順番に表示されます)。

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

確かに、それは正しくないかもしれませんが、動作します。


8
降順のリストにソートする場合は、OrderByDescendingを使用することもできます。
麺徳祭2011

私はうまくいきましたが、少し変更する必要がありました。
Josh、

17
この「動作」は保証されていません。その実装の詳細。他の時間に動作する必要はありません。間違った答え、反対投票。
nawfal 14

4
ディクショナリの出力は、特定の並べ替え順序を持つことが保証されていません。
ロジャーウィルコックス2015年

5
量産コードでこれを見ることは非常に心配です。これは保証されておらず、いつでも変更される可能性があります。私が実用的なソリューションを敬遠しているわけではありません。それは、データ構造imoの理解の欠如を示しているだけです。
jamespconnor 2015

61

高レベルでは、辞書全体を調べて各値を確認する以外に選択肢はありません。

多分これは役立つ: 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-> string> nextPair stringfirstPair-> string> firstPair
アート

2
Linq以外の完璧なソリューション。問題の解決が絶対に必要ない場合でも、Linqを使用する必要性を人々がどのように感じているかを驚かせることは決してありません。C#3では、並べ替えを単純化してラムダを使用することもできると思います。myList.Sort((x、y)=> x.Value.CompareTo(y.Value));

25

とにかく辞書を並べ替えることはできません。実際には注文されていません。ディクショナリの保証は、キーと値のコレクションが反復可能であり、インデックスまたはキーによって値を取得できることですが、特定の順序は保証されていません。したがって、名前と値のペアをリストに入れる必要があります。


2
ソートされたディクショナリは、キーと値のペアのリストを生成します。
再帰的

1
@recursiveどのディクショナリもそれを生成する必要があります。興味深いが、私の回答は正しいが不完全である(より良い例が実行されたはずである)が無効な回答の下に投票され、元のディクショナリの重複する値で例外が発生することに注意してください(キーは一意であり、値は保証されません) to be)
ロジャー・ウィルコックス2010

5
辞書はソートできないため、これはbesの答えです。キーをハッシュし、非常に高速なシーク操作を実行できます。
Paulius Zaliaduonis、2011

@NetMageはい。しかし、問題のもう1つの部分は、Valueで並べ替えたいということです。そして、あなたはキーとバリューを交換することによってのみそれを行うことができました。値は必ずしも一意である必要はありませんが、キーは一意である必要があります。
ロジャーウィルコックス

はい、しかし私はそれの絶対的な陳述のためにあなたの答えは正しくないと思います。
NetMage

21

辞書のエントリは並べ替えません。.NETのディクショナリクラスはハッシュテーブルとして実装されます-このデータ構造は定義によりソートできません。

(キーで)コレクションを反復処理できるようにする必要がある場合は、バイナリ検索ツリーとして実装されているSortedDictionaryを使用する必要があります。

ただし、ソース構造は別のフィールドでソートされるため、ソース構造は関係ありません。それでも、頻度でソートして、関連するフィールド(頻度)でソートされた新しいコレクションに入れる必要があります。したがって、このコレクションでは、頻度はキーであり、単語は値です。多くの単語が同じ頻度を持つ可能性があるため(これをキーとして使用する予定)、DictionaryもSortedDictionaryも使用できません(一意のキーが必要です)。これにより、SortedListが残ります。

メイン/ファーストディクショナリの元のアイテムへのリンクを維持し続ける理由を理解できません。

コレクション内のオブジェクトがより複雑な構造(より多くのフィールド)を持ち、キーとしていくつかの異なるフィールドを使用してそれらに効率的にアクセス/ソートできるようにする必要がある場合-主ストレージで構成されるカスタムデータ構造がおそらく必要になりますO(1)の挿入と削除(LinkedList)といくつかのインデックス構造-Dictionaries / SortedDictionaries / SortedListsをサポートします。これらのインデックスは、複雑なクラスのフィールドの1つをキーとして使用し、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

ソートされた辞書を取得する最も簡単な方法は、組み込み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この場合は役に立ちません。
Marty Neal

ええと...彼/彼女(あなた)ができるなら、単に値をキーとして設定してください。私は操作の時間を計り、sorteddictionary()常に少なくとも1マイクロ秒で勝ち取りました、そしてそれは管理するのがはるかに簡単です(それを辞書と同様に簡単に対話して管理するものに戻す変換のオーバーヘッドは0です(すでにですsorteddictionary))。
mbrownnyc 2013年

2
@mbrownnyc-いいえ、そのためには、VALUESが一意であるという前提または前提条件が必要ですが、これは保証されていません。
ロジャーウィルコックス2015年

6

値で並べ替えられた「一時的な」リストを作成するだけであれば、他の回答も適しています。ただし、によって並べ替えられたディクショナリを、によって並べ替えられKeyた別のディクショナリと自動的に同期させるValue場合は、Bijection<K1, K2>クラスを使用できます。

Bijection<K1, K2> 2つの既存のディクショナリでコレクションを初期化できるため、一方をソートせず、もう一方をソートしたい場合は、次のようなコードでバイジェクションを作成できます。

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に似ていますが、各DictionaryをSortedDictionaryに置き換えることができます。答えは重複する値をサポートしていないように見えますが(1から1を想定しています)。
crokusek

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#では、dictionaries 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
}

あなたは1つのトリックを行うことができ、

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

または

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

それは、格納している値の種類にも依存します。
単一(文字列、intなど)または複数(List、Array、ユーザー定義クラスなど)のいずれ
かで、単一のリストを作成してから並べ替えを適用できます。
ユーザー定義クラスの場合、そのクラスはIComparableを実装し 、LINQよりも高速でオブジェクト指向であるため
ClassName: IComparable<ClassName>オーバーライドcompareTo(ClassName c)する必要があります。


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

並べ替え:

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

2
そして、これがすでに回答されているのに、なぜこの回答を追加するのですか?
nawfal 2013年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.