辞書を反復処理するための最良の方法は何ですか?

2008年09月27日に質問されました。  ·  閲覧回数 1.7M回  ·  ソース

Jake Stewart picture
2008年09月27日

C#で辞書を反復処理するいくつかの異なる方法を見てきました。 標準的な方法はありますか?

回答

Pablo Fernandez picture
2008年09月27日
3898
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}
Jacob picture
2008年09月27日
914

別の言語の連想配列を使用するように、C#で汎用辞書を使用しようとしている場合:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

または、キーのコレクションを反復処理するだけでよい場合は、

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

そして最後に、値のみに関心がある場合:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

varキーワードはオプションのC#3.0以降の機能であることに注意してください。ここでは、キー/値の正確なタイプを使用することもできます)

Maur&#237;cio Fedatto picture
2011年03月11日
166

場合によっては、forループの実装によって提供されるカウンターが必要になることがあります。 そのために、LINQはElementAtを提供します。これにより、次のことが可能になります。

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
J Healy picture
2008年09月27日
103

キーまたは値のどちらを探しているかによって異なります...

MSDN Dictionary(TKey, TValue)クラスの説明:

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}
St&#233;phane Gourichon picture
2015年08月10日
85

一般的に、特定のコンテキストなしで「最良の方法」を求めることは、最良の色は何であるかを尋ねるような

一方で、色はたくさんあり、最高の色はありません。 それは必要性に依存し、しばしば味にも依存します。

一方、C#で辞書を反復処理する方法はたくさんあり、最善の方法はありません。 それは必要性に依存し、しばしば味にも依存します。

最も簡単な方法

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

値のみが必要な場合( itemと呼ぶことができ、 kvp.Valueよりも読みやすくなります)。

foreach (var item in items.Values)
{
    doStuff(item)
}

特定の並べ替え順序が必要な場合

一般的に、初心者は辞書の列挙の順序に驚いています。

LINQは、順序(および他の多くのもの)を指定できる簡潔な構文を提供します。例:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

繰り返しますが、必要なのは値だけかもしれません。 LINQは、次のような簡潔なソリューションも提供します。

  • 値を直接反復します( itemと呼ぶことができ、 kvp.Valueよりも読みやすくなります)
  • しかし、キーでソートされています

ここにあります:

foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

これらの例から実行できる実際のユースケースは他にもたくさんあります。 特定の注文が必要ない場合は、「最も簡単な方法」(上記を参照)に固執してください。

George Mauer picture
2008年09月27日
56

foreachが標準的な方法だと思いますが、それは明らかにあなたが探しているものに依存します

foreach(var kvp in my_dictionary) {
  ...
}

それはあなたが探しているものですか?

Jaider picture
2019年01月08日
47

C#7.0ではコンストラクタ導入されました。.NETCore2.0+アプリケーションを使用している場合、構造体KeyValuePair<>すでにDeconstruct()れています。 だからあなたはすることができます:

var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
    Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}

enter image description here

Onur picture
2015年06月11日
44

マルチスレッド処理用の大きな辞書でこれを試すこともできます。

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});
theo picture
2008年09月27日
30

たくさんのオプションがあります。 私の個人的なお気に入りはKeyValuePairによるものです

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

キーと値のコレクションを使用することもできます

Liath picture
2014年07月30日
30

この質問にはすでに多くの回答が寄せられていることを感謝しますが、少し調査を行いたいと思いました。

辞書の反復は、配列のようなものの反復と比較すると、かなり遅くなる可能性があります。 私のテストでは、配列での反復には0.015003秒かかりましたが、辞書(同じ数の要素を持つ)での反復には0.0365073秒かかりました。これは2.4倍の長さです。 私ははるかに大きな違いを見てきましたが。 比較のために、リストは0.00215043秒の中間にありました。

しかし、それはリンゴとオレンジを比較するようなものです。 私のポイントは、辞書の反復は遅いということです。

辞書はルックアップ用に最適化されているので、それを念頭に置いて2つのメソッドを作成しました。 1つは単にforeachを実行し、もう1つはキーを繰り返して検索します。

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

これはキーをロードし、代わりにそれらを繰り返し処理します(キーをstring []にプルしようとしましたが、違いはごくわずかでした。

public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}

この例では、通常のforeachテストに0.0310062がかかり、キーバージョンには0.2205441がかかりました。 すべてのキーをロードしてすべてのルックアップを繰り返すと、明らかにかなり遅くなります。

最後のテストでは、ここでキーを使用することに利点があるかどうかを確認するために、反復を10回実行しました(この時点で、私はただ興味がありました)。

何が起こっているのかを視覚化するのに役立つ場合は、RunTestメソッドを次に示します。

private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}

ここでは、通常のforeachの実行に0.2820564秒かかりました(予想どおり、1回の反復にかかった時間の約10倍)。 キーの反復には2.2249449秒かかりました。

追加のために編集:他の回答のいくつかを読んだことで、辞書の代わりに辞書を使用した場合にどうなるか疑問に思いました。 この例では、配列は0.0120024秒、リストは0.0185037秒、ディクショナリは0.0465093秒かかりました。 データ型が辞書の速度に影響を与えると予想するのは合理的です。

私の結論は何ですか?

  • 可能であれば、ディクショナリを反復処理することは避けてください。同じデータを含む配列を反復処理するよりも大幅に遅くなります。
  • 辞書を反復処理することを選択した場合は、賢くなりすぎないようにしてください。ただし、標準のforeachメソッドを使用するよりも遅くなる可能性があります。