C#で辞書を反復処理するいくつかの異なる方法を見てきました。標準的な方法はありますか?
C#で辞書を反復処理するいくつかの異なる方法を見てきました。標準的な方法はありますか?
回答:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
var
タイプがわからないときに@OzairKafrayを使用することは、一般的に悪い習慣です。
var
いるように、型がコンパイル時にわかっている場合にのみ機能します。Visual Studioがタイプを認識している場合は、それを調べることもできます。
別の言語で連想配列を使用するような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以上の機能であり、ここではキー/値の正確なタイプを使用することもできます)
myDictionary
(もちろん、実際の名前でない限り)。タイプが明らかな場合などは、varを使用するのが適切だと思いますvar x = "some string"
が、すぐに明らかでない場合は、コードリーダー/レビューアーを傷つけるのは遅延コーディングだと思います
var
私の意見では、控えめに使用する必要があります。特にここでは、それは建設的ではありません。タイプKeyValuePair
はおそらく質問に関連しています。
var
独特の目的があり、それが「構文的」な砂糖だとは信じていません。それを意図的に使用することは適切なアプローチです。
場合によっては、forループの実装によって提供されるカウンターが必要になることがあります。そのために、LINQはElementAt
以下を可能にするものを提供します。
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
ElementAt
O(n)操作ではありませんか?
.ElementAt
このコンテキストで使用すると微妙なバグが発生する可能性があります。はるかに深刻なのは、上記のArturoのポイントです。dictionary.Count + 1
O(n)のみであるはずの操作に対して、O(n ^ 2)の複雑さにつながる辞書時間を反復します。インデックスが本当に必要な場合(必要な場合は、最初に間違ったコレクション型を使用している可能性があります)、dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
代わりに反復し.ElementAt
、ループ内では使用しないでください。
キーと値のどちらを使用するかによって異なります...
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);
}
一般に、特定のコンテキストなしで「最良の方法」を求めることは、何が最良の色であるかを尋ねるような ものです。
一方で、色はたくさんあり、最高の色はありません。それは必要性に依存し、しばしば味にも依存します。
一方、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)
}
これらの例から実行できる、より多くの実際の使用例があります。特定の注文が必要ない場合は、「最も簡単な方法」(上記を参照)を使用してください。
.Values
はselect句ではなく、すべきです。
Value
フィールドがありません。私がここで見る正確なタイプはIOrderedEnumerable<KeyValuePair<TKey, TValue>>
です。多分あなたは何か他のものを意味しましたか?あなたが意味することを示す完全な行を書くことができますか(そしてそれをテストしてください)?
items.Value
あなたが提案したように私は書きました。コメントした4番目のセクションの場合、これSelect()
は、foreach
キーと値のペアではなく、ディクショナリの値を直接列挙する方法です。Select()
この場合、どうしてもが気に入らない場合は、3番目のコードセクションを使用することをお勧めします。4番目のセクションの要点は、コレクションをLINQで前処理できることを示すことです。
.Keys.Orderby()
、キーのリストを反復処理します。それで十分なら、結構です。値が必要な場合は、ループで各キーの辞書をクエリして値を取得する必要があります。多くのシナリオでは、実際的な違いはありません。高性能のシナリオでは、そうなります。答えの冒頭で書いたように、「多くの方法があり(...)、最善の方法はありません。それは、ニーズにも依存し、好みにも依存します。」
foreachが標準的な方法だと思いますが、それは明らかにあなたが探しているものに依存します
foreach(var kvp in my_dictionary) {
...
}
それはあなたが探しているものですか?
kvp
は、辞書や関連データ構造を反復するときにKeyValuePairインスタンスに名前を付けるために一般的に使用されますforeach(var kvp in myDictionary){...
。
C#7.0はDeconstructorsを導入しました。.NETCore 2.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}");
}
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
この質問にはすでに多くの回答があったことを感謝しますが、少し調査を行いたかったのです。
ディクショナリの反復は、配列のようなものの反復と比較すると、かなり遅くなる可能性があります。私のテストでは、配列の反復には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秒かかりました。データ型によって、ディクショナリの速度がどれほど遅くなるかを予測できるのは当然です。
私の結論は何ですか?
では.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;
}
}
ValueTuple
組み込まれています。以前のバージョンのnugetパッケージとして使用できます。さらに重要なことに、Deconstruct
メソッドがのデコンストラクタとして機能するには、C#7.0以降が必要ですvar (fruit, number) in fruits
。
C#7以降、オブジェクトを変数に分解できます。これは、辞書を反復処理するための最良の方法だと思います。
例:
KeyValuePair<TKey, TVal>
それを分解する拡張メソッドを作成します。
public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
key = pair.Key;
value = 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}");
}
以下を反復することを提案しました
Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary) {
//Do some interesting things;
}
参考までにforeach
、値がオブジェクト型の場合は機能しません。
foreach
機能しませんか?そうでなければ、これはあまり意味がありません。object
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'
}
拡張メソッドはのKeyValuePair
をIDictionary<TKey, TValue>
強く型付けされたに変換し、tuple
この新しい快適な構文を使用できるようにします。
必要なディクショナリエントリだけをtuples
に変換するためtuples
、ディクショナリ全体はに変換されないため、これに関連するパフォーマンスの問題はありません。
tuple
を使用する場合と比較して、を作成するための拡張メソッドの呼び出しにはわずかなコストしかかかりKeyValuePair
ません。これは、KeyValuePair
のプロパティKey
とValue
新しいループ変数を割り当てている場合は問題になりません。
実際には、この新しい構文はほとんどの場合に非常に適しています。ただし、特定の場所でそれを使用しないオプションがまだある低レベルの超高パフォーマンスシナリオは除きます。
これをチェックしてください:MSDNブログ-C#7の新機能
kvp.Key
を使用し、kvp.Value
キーと値をそれぞれ使用する必要があります。タプルを使用すると、foreachブロック内でさらに変数宣言を使用することなく、キーと値に自由に名前を付けることができます。たとえば、キーにfactoryName
、値にと名前を付けることができますmodels
。これは、ネストされたループ(辞書の辞書)を取得するときに特に役立ちます。コードのメンテナンスがはるかに簡単になります。ぜひお試しください!;-)
私はこれが非常に古い質問であることを知っていますが、役に立つかもしれないいくつかの拡張メソッドを作成しました:
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););
値を列挙するだけでよい場合は、辞書の値コレクションを使用します。
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
このメソッドは、MSDNのDictionaryBaseクラスのドキュメントで見つかりました。
foreach (DictionaryEntry de in myDictionary)
{
//Do some stuff with de.Value or de.Key
}
これは、DictionaryBaseから継承したクラスで正しく機能する唯一のものでした。
Hashtable
オブジェクト
ContainsKey()
にfor
バージョン?これにより、比較対象のコードにはないオーバーヘッドが追加されます。TryGetValue()
「キーが存在する場合、アイテムをキーで取得する」パターンを置き換えるために存在します。さらに、dict
から0
までの連続した整数の範囲が含まれている場合dictCount - 1
、インデクサーは失敗しないことがわかります。それ以外の場合は、dict.Keys
反復する必要があります。どちらの方法でも、ContainsKey()
/はTryGetValue()
必要ありません。最後に、コードのスクリーンショットを投稿しないでください。
辞書をループする拡張機能を作成しました。
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));
ForEach
が持っているメソッドを定義しましたforeach (...) { }
...不要のようです。
たとえば、デフォルトで値コレクションを反復処理したい場合、IEnumerable <>を実装できると思います。Tはディクショナリ内の値オブジェクトのタイプで、「this」はディクショナリです。
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
ほとんどの答えはforeachループに関連しているので、ちょうど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とsomeList.Foreach(){}で指摘されているように、パフォーマンスが若干向上する可能性があります。オプション/はまったく効果がありません。
また、foreachループ内の「Value」プロパティに値を割り当てることができないことに注意してください。一方、「キー」も操作できるため、実行時に問題が発生する可能性があります。
キーと値を「読み取る」だけの場合は、IEnumerable.Select()を使用することもできます。
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
foreach
ます。副作用の可視性を強制します。
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
AggregateObject
追加しKeyValuePair
ますか?質問で要求されている「反復」はどこにありますか?
foreach
が、私は何度も使用しています。私の回答は本当に反対票に値しましたか?
Select
使用して結果に影響を与えますが、それ自体はイテレーターではありません。反復(foreach
)が使用されるもののタイプ(特に副作用のある操作)は、Linqの範囲外Select
です。ラムダaggregateObjectCollection
は、実際に列挙されるまで実行されません。この回答を「最初のパス」(つまり、ストレートの前に使用foreach
)とすると、悪い習慣が助長されます。状況によっては、ディクショナリを反復する前に役立つLinq操作があるかもしれませんが、これは要求された質問には対応していません。
Dictionary <TKey、TValue>これはc#のジェネリックコレクションクラスであり、データをキー値形式で格納します。キーは一意である必要があり、nullにすることはできませんが、値は重複してnullにすることができます。ディクショナリ内の各アイテムはキーとその値を表すKeyValuePair <TKey、TValue>構造体として扱われます。したがって、要素の反復中に要素型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);
}
forループを使用したい場合、これを行うことができます:
var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
var key= keyList[i];
var value = dictionary[key];
}
foreach
ループ とために悪いパフォーマンスをnew List<string>(dictionary.Keys)
繰り返すだろうdictionary.Count
、あなたもそれを自分で反復する機会を持つ前に、時間を。「最善の方法」を求めることは主観的であることはさておき、質問が求める「最善の方法」または「標準的な方法」のどちらにこれが当てはまるかはわかりません。「forループを使用したい場合...」には、「ループを使用しない」と対抗しfor
ます。
foreach (var pair in dictionary.ToArray()) { }
。それでも、このコードを使用する特定のシナリオと、その使用の影響を回答で明確にするとよいと思います。
この回答ですでに指摘したように、.NET Core 2.0、.NET Standard 2.1、および.NET Framework 5.0(プレビュー)から始まるメソッドをKeyValuePair<TKey, TValue>
実装Deconstruct
します。
これにより、KeyValuePair
不可知論的な方法で辞書を反復処理することが可能です。
var dictionary = new Dictionary<int, string>();
// ...
foreach (var (key, value) in dictionary)
{
// ...
}
linqでシンプル
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "American Ipa");
dict.Add(2, "Weiss");
dict.ToList().ForEach(x => Console.WriteLine($"key: {x.Key} | value: {x.Value}") );
使用の間で議論がある最高ランクの投稿に加えて
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
または
foreach(var entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
初期化からディクショナリタイプを確認できるため、最も完全なものは次のとおりです。kvpはKeyValuePairです。
var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x
foreach(var kvp in myDictionary)//iterate over dictionary
{
// do something with kvp.Value or kvp.Key
}