web-dev-qa-db-ja.com

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

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

2249
Jake Stewart
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}
3260
Pablo Fernandez

C#で一般的なDictionaryを使用しようとしているのであれば、他の言語の連想配列を使用します。

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以上の機能ですので、ここでも正確なタイプのキー/値を使用できます。)

743
Jacob

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

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
124

あなたがキーまたは値の後にいるかどうかに依存します...

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);
}
82
J Healy

一般的に、特定の文脈なしで「最善の方法」を尋ねることは、最高の色は何であるかを尋ねることと同じです。

一方は色がたくさんあり、最高の色はありません。それは必要性によって、そしてしばしば好みによっても異なります。

一方、C#でDictionaryを反復処理する方法は数多くありますが、最善の方法はありません。それは必要性によって、そしてしばしば好みによっても異なります。

最も簡単な方法

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

これらの例からあなたができる現実的なユースケースはもっとたくさんあります。あなたが特定の順序を必要としないならば、単に「最も簡単な方法」(上記を見てください)に固執してください!

70

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

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

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

45
George Mauer

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

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});
34
Onur

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

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

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

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

24
theo

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

辞書を反復処理することは、配列のようなものを反復処理することと比べると、かなり遅くなる可能性があります。私のテストでは、配列の繰り返し処理は0.015003秒かかりましたが、ディクショナリの繰り返し処理(要素数が同じ)は0.0365073秒かかりました。これは2.4倍の長さです。私ははるかに大きな違いを見ましたが。比較のために、リストはその間のどこかに0.00215043秒でした。

しかし、それはりんごとオレンジを比較するようなものです。私のポイントは、辞書を繰り返し処理するのは遅いということです。

辞書は検索用に最適化されているので、それを念頭に置いて2つの方法を作成しました。一方は単にforeachを実行し、もう一方はキーを反復して検索します。

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

    return "Normal";
}

これはキーをロードして代わりにそれらを反復処理します(私はキーを文字列[]に引き込むことも試みましたが、その違いはごくわずかでした)。

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でした。すべてのキーをロードしてすべての検索を繰り返すことは、明らかにLOTが遅くなります。

最後のテストでは、ここでキーを使用することに利点があるかどうかを確認するために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秒かかりました。

追加の編集: /他の答えのいくつかを読むと、Dictionaryの代わりにDictionaryを使用した場合にどうなるか疑問に思いました。この例では、配列に0.0120024秒、リストに0.0185037秒、辞書に0.0465093秒かかりました。データ型によって、ディクショナリの速度が大幅に遅くなることが予想されます。

私の結論は

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

C#7.0が導入されました デコンストラクタ そしてあなたが.NET Core 2.0+ Applicationを使っているなら、struct 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

13
Jaider

あなたは反復するために以下に提案しました

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

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

参考までに、値がオブジェクト型の場合、foreachは機能しません。

11
Khushi

.NET Framework 4.7を使うと 分解 を使うことができます。

var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}

このコードをより低いC#バージョンで動作させるには、System.ValueTuple NuGet packageを追加してどこかに書いてください。

public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> Tuple,
        out T1 key, out T2 value)
    {
        key = Tuple.Key;
        value = Tuple.Value;
    }
}
10
Pavel

C#7 を使用して、この 拡張メソッド をソリューションの任意のプロジェクトに追加します。

public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


そして、この単純な構文を使います

foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


または、これが好きなら

foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


伝統的なものの代わりに

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


拡張メソッドはあなたのIDictionary<TKey, TValue>KeyValuePairを強く型付けされたTupleに変換し、この新しい快適な構文を使えるようにします。

必要な辞書項目をtuplesに変換するので、辞書全体をtuplesに変換することはありません。したがって、それに関するパフォーマンス上の問題はありません。

Tupleを直接使用する場合と比較してKeyValuePairを作成するための拡張メソッドを呼び出すためのわずかなコストがあります。KeyValuePairのプロパティKeyValueを新しいループ変数に代入する場合は問題になりません。

実際には、この新しい構文はほとんどの場合に非常に適しています。ただし、その特定の場所では単純に使用しないという選択肢が依然として存在する、低レベルの超高性能シナリオを除きます。

これをチェックしてください: MSDNブログ - C#7の新機能

8

辞書を繰り返すための最も簡単な形式:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}
8
Ron

列挙する値だけが必要な場合は、辞書の値コレクションを使用してください。

foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}

それが最速の方法であることを述べているこの記事で報告: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html

6
ender

私はMSDNのDictionaryBaseクラスのドキュメントでこのメソッドを見つけました:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

これは、DictionaryBaseから継承したクラスで正しく機能することができた唯一のものです。

5
Zannjaminderson

C#7では、オブジェクトを変数に分解することができます。私はこれが辞書を反復処理するための最良の方法であると信じています。

例:

それを分解する拡張メソッドをKeyValuePair<TKey, TVal>上に作成します。

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey, out TVal val)
{
   key = pair.Key;
   val = pair.Value;
}

以下の方法で、任意のDictionary<TKey, TVal>を繰り返します。

// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}
3
Domn Werner

MSDNの公式文書によると、Dictionaryを反復処理する標準的な方法は次のとおりです。

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}
3
Nick

私は.NET 4.0以降を利用して、最初に受け入れられたものに対する最新の回答を提供します。

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}
3
yazanpro

たとえば、デフォルトでvaluesコレクションを反復したいのであれば、IEnumerable <>を実装することができます。ここで、Tはディクショナリ内のvaluesオブジェクトの型、 "this"はDictionaryです。

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}
2
mzirino

これは非常に古い質問であることは知っていますが、便利な拡張メソッドをいくつか作成しました。

    public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
    {
        foreach (KeyValuePair<T, U> p in d) { a(p); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
    {
        foreach (T t in k) { a(t); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
    {
        foreach (U u in v) { a(u); }
    }

このようにして、次のようなコードを書くことができます。

myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
myDictionary.Keys.ForEach(key => Console.Write(key););
myDictionary.Values.ForEach(value => Console.Write(value););
2
boca

辞書をループするように拡張機能を書きました。

public static class DictionaryExtension
{
    public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
        foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
            action(keyValue.Key, keyValue.Value);
        }
    }
}

それからあなたは呼び出すことができます

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));
1
Steven Delrue
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));
1
Pixar

ほとんどの答えはforeach-loopに関するものなので、ちょうど2セントを追加したいと思いました。次のコードを見てください。

Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});

これが '.ToList()'の追加呼び出しを追加すると考えられますが、大きな辞書を扱う場合は特に、ここで指摘されているように(ここで指摘したように foreach vs someList.Foreach(){} )少しパフォーマンスが向上します並行して実行することはできません/まったく効果がありません。

また、foreachループ内で 'Value'プロパティに値を代入することはできませんのでご注意ください。その一方で、あなたは 'Key'も同様に操作することができるでしょう、おそらくあなたは実行時にあなたをトラブルに巻き込ませるでしょう。

キーと値を「読み」たいだけの場合は、IEnumerable.Select()も使用できます。

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
1
Alex

Dictionary <TKey、TValue> c#のジェネリックコレクションクラスであり、データをキー値の形式で格納します。Keyは一意である必要があり、nullにすることはできません。一方、valueは重複およびnullにすることができます。辞書の中のはKeyValuePair <TKey、TValue>構造体として扱われ、キーとその値を表します。したがって、elementの繰り返し中は、要素型KeyValuePair <TKey、TValue>を使用する必要があります。以下はその例です。

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}
0

辞書を反復する3つの異なる方法.

方法1 - forループを使用する

for (int i = 0; i < DemotestDictionary.Count; i++ )
 {
 int key = i;
 string value = DemotestDictionary[i];
 // Do something here
 }

方法2 - foreachの繰り返しを使用する

foreach (var entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
 // Do something here
 } 

方法3 - KeyValuePairを使用して反復を繰り返す

foreach (KeyValuePair<int, string> entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
// Do something here
 }
0