2010-10-25 104 views
17

我正在C#中開發一個以.NET 3.5爲目標的應用程序。其中,我有2個類似的字典,其中包含我的應用中特定元素集的驗證條件。兩本字典都有相同的簽名。第一個詞典具有默認設置,第二個詞典包含一些用戶定義的設置。C#合併2個字典

var default_settings = new Dictionary<string, MyElementSettings>(); 
var custom_settings = new Dictionary<string, MyElementSettings>(); 

我想將2個字典組合成一個包含兩個字典元素的字典。

我遇到的問題是兩個字典都可能有一些相同的鍵值。我想要的基本規則是將這兩個詞典組合起來,並且如果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個字典的查詢,但大多數都涉及用重複鍵對任何元素進行分組,或者只返回一個只有重複鍵的集合/是否有一個LINQ查詢或表達式可以模仿foreach循環的行爲我在用?

回答

38

兩點:

  1. LINQ是不是偉大的執行的副作用。在這種情況下,你試圖改變現有的集合而不是執行查詢,所以我會避開純粹的LINQ解決方案。
  2. 如果密鑰不存在,setter on the generic dictionary's indexer已經具有添加鍵值對的效果,或者如果該值存在,則覆蓋該值。

當您設置的屬性值,如果 關鍵是在解釋,與 相關的值鍵被分配 值替換。如果密鑰不在 字典中,則密鑰和 值將添加到字典中。

所以你foreach循環基本上等同於:

foreach (var item in custom_settings) 
{ 
    default_settings[item.Key] = item.Value; 
} 

現在是相當簡潔了,所以我不認爲LINQ是要幫助你所有的東西。

+3

+1點#2(http://msdn.microsoft.com/en-us/library/k7z0zy8k.aspx) – Brad 2010-10-25 13:59:12

+1

+1教我的東西我不知道。我相對自我教育與C#,並不知道你可以用字典做到這一點。 – psubsee2003 2010-10-25 14:09:54

+0

@ psubsee2003:歡呼聲。我很確定我使用了與您的代碼相同的模式,然後才發現它是多餘的(偶然,正在用反射器探索)。 – Ani 2010-10-25 14:17:17

2

如果你要這個做了很多,那麼我建議你寫字典鍵的相等比較:

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

這一個爲我工作,即使沒有'KeyEqualityComparer'類/參數。謝謝 – AceMark 2012-03-15 03:28:26

1

我認爲我最初選擇的答案仍然是這個特定情況下的最佳答案,我發現自己處於另一個類似的情況,前一段時間我想將對象轉換爲字典併合併到一起類似的方式,所以我想我會在這裏添加該解決方案來幫助未來的人。我找到了一種新方法,而不是將它們轉換爲字典並在所選答案中使用該方法。

我實際上在SE-CodeReview上發佈了initial solution,實際上有一個建議來進一步細化它。下面是我用最後的代碼:

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

如果您不能覆蓋FooEquals(),那麼另一種選擇是使用Union()

GroupBy()代替
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; 
} 
8

這是一個很好的基於Ani答案的擴展方法。

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; 
     } 
    } 
} 
+1

我會更改字典在IDictionary – 2015-08-23 11:24:39

+0

如果這是一個字典集合'IEnumerable <字典>'如何能匹配它們? – barteloma 2017-08-15 13:09:49