.NET 3.5を対象としたC#でアプリを開発しています。その中に、アプリ内の特定の要素セットの検証基準を含む2つの同様の辞書があります。両方の辞書は同じ署名を持っています。最初のディクショナリにはデフォルト設定があり、2番目のディクショナリにはいくつかのユーザー定義設定が含まれています。
var default_settings = new Dictionary<string, MyElementSettings>();
var custom_settings = new Dictionary<string, MyElementSettings>();
2つの辞書を組み合わせて、両方の辞書の要素を含む1つにしたいと思います。
私が遭遇している問題は、両方の辞書が同じキー値のいくつかを持っている可能性があるということです。私が望む基本的なルールは、両方のディクショナリを組み合わせることです。custom_settingsにすでにdefault_settingsに存在するキーがある場合、custom_settings値はdefault_settings値を上書きします。私が持っている最善の解決策は、foreachループだけで、キーが他の辞書に存在するかどうかを確認し、存在しない場合は追加します。
foreach (var item in custom_settings)
{
if (default_settings.ContainsKey(item.Key))
default_settings[item.Key] = item.Value;
else
default_settings.Add(item.Key, item.Value);
}
私はいくつかの基本的なLINQクエリを実行しましたが、さらに高度なものの学習に取り組んでいます。 2つの辞書をマージするクエリをいくつか見てきましたが、ほとんどの場合、重複するキーを持つ要素をグループ化するか、重複するキーのみを持つコレクションのみを返します/ foreachループの動作を模倣するLINQクエリまたは式はありますか使ってます?
2つのポイント:
プロパティ値を設定するときに、キーがディクショナリにある場合、そのキーに関連付けられている値は、割り当てられた値に置き換えられます。キーがディクショナリにない場合、キーと値がディクショナリに追加されます。
したがって、foreach
ループは基本的に次と同等です。
foreach (var item in custom_settings)
{
default_settings[item.Key] = item.Value;
}
すでにかなり簡潔になっているので、LINQがそれほど役立つとは思いません。
これは、Aniの回答に基づいたNice拡張方法です。
public static class DictionaryExtensionMethods
{
public static void Merge<TKey, TValue>(this Dictionary<TKey, TValue> me, Dictionary<TKey, TValue> merge)
{
foreach (var item in merge)
{
me[item.Key] = item.Value;
}
}
}
これを頻繁に行う場合は、辞書キーの等価比較子を作成することをお勧めします。
private class KeyEqualityComparer<T, U> : IEqualityComparer<KeyValuePair<T, U>>
{
public bool Equals(KeyValuePair<T, U> x, KeyValuePair<T, U> y)
{
return x.Key.Equals(y.Key);
}
public int GetHashCode(KeyValuePair<T, U> obj)
{
return obj.Key.GetHashCode();
}
}
そして、辞書をマージする必要があるときはいつでも、次のことができます
var comparer = new KeyEqualityComparer<string, MyElementSettings>();
dict1 = dict1.Union(dict2,comparer).ToDictionary(a => a.Key, b => b.Value);
私が最初に選択した答えは、この特定のケースの最良の答えであると思います。少し前に、辞書に変換してマージしたい2つのIEnumerable<>
オブジェクトがあった別の同様の状況に陥りました。同様の方法なので、将来誰かを助けるために、ここにそのソリューションを追加すると思いました。両方を辞書に変換して、選択した回答のメソッドを使用するのではなく、新しいアプローチを見つけました。
私は実際に 初期ソリューション をSE-CodeReviewに投稿し、実際にそれをさらに改良するための提案をしました。これが私が使用した最終的なコードです:
public Dictionary<String, Foo> Merge(XElement element1, XElement element2)
{
IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML
IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML
var result = firstFoos.Union(secondFoos).ToDictionary(k=>k.Name, v=>v);
return result;
}
public class Foo
{
public String Name { get; }
// other Properties and Methods
// .
// .
// .
public override Boolean Equals(Object obj)
{
if (obj is Foo)
{
return this.Name == ((Foo)obj).Name;
}
return false;
}
}
これの鍵は、Foo
がEquals()
をオーバーライドして、重複と見なすことができるFoo
オブジェクトを定義する必要があることです。また、重複するオブジェクトを定義するメンバーもDictionary<>
キー(この場合はName
)
Foo
のEquals()
をオーバーライドできない場合、他のオプションはConcat()
の代わりにGroupBy()
とUnion()
を使用することです。
public Dictionary<String, Foo> Merge(XElement element1, XElement element2)
{
IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML
IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML
var result = firstFoos.Concat(secondFoos)
.GroupBy(foo => foo.Name)
.Select(grp => grp.First())
.ToDictionary(k=>k.Name, v=>v);
return result;
}