2010-01-25 54 views
2

這與this question有關,關於如何合併C#中的兩個詞典。展示了一個優雅的Linq解決方案,這很酷。在C#中包含列表的合併詞典#

然而,這一問題涉及到Dictionary<Object1, Object2>,,而我有一本字典,其中值是List<Object2>.

我要尋找一個解決方案,合併有以下要求Dictionary<Object1, List<Object2>>,

  • 如果Dictionary1包含與Dictionary2相同的密鑰,那麼它們的List<Object2>列表應該組合。您最終將得到一個帶有共享密鑰的新鍵 - 值對,以及來自兩個詞典的組合列表。
  • 如果Dictionary1包含一個Dictionary2不存在的鍵,則Dictionary1中的List<Object2>列表應該成爲該值,反之亦然。

這可能不是Linq中是不可能的,或者它可能是值得寫出來與草書for循環之類的,但它會是不錯的一個優雅的解決方案。

回答

3

我會建議創建自己的擴展方法。它將更加高效和易於修改。

public static void MergeDictionaries<OBJ1, OBJ2>(this IDictionary<OBJ1, List<OBJ2>> dict1, IDictionary<OBJ1, List<OBJ2>> dict2) 
    { 
     foreach (var kvp2 in dict2) 
     { 
      // If the dictionary already contains the key then merge them 
      if (dict1.ContainsKey(kvp2.Key)) 
      { 
       dict1[kvp2.Key].AddRange(kvp2.Value); 
       continue; 
      } 
      dict1.Add(kvp2); 
     } 
    } 
1

難點在於處理關鍵衝突的合併。

如果我們首先使用SelectMany展開所有輸入詞典,那麼我們可以通過鍵將它們組合在一起。

var result = dictionaries 
    .SelectMany(dict => dict) 
    .GroupBy(kvp => kvp.Key) 

結果集包含基團,其中每個組的關鍵是從原來的字典的密鑰,並且該組的內容具有相同的鍵的清單的IEnumerable<List<T>>。從這些組中,我們可以將所有List<T>合併成一個單獨的IEnumerable<T>,使用轉換與SelectMany

var result = dictionaries 
    .SelectMany(dict => dict) 
    .GroupBy(kvp => kvp.Key) 
    .Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)}) 

然後,我們可以得到這樣一個字典使用ToDictionary改造,轉換IEnumerable<T>List<T>

var result = dictionaries 
    .SelectMany(dict => dict) 
    .GroupBy(kvp => kvp.Key) 
    .Select(grp => new { Key = grp.Key, Items = grp.SelectMany(list => list)}) 
    .ToDictionary(kip => kip.Key, kip => new List<T>(kip.Items)); 

更新迴應置評

可以填充dictionaries但是你喜歡。我假定它是一種類型,它實現IEnumerable<IDictionary<TKey, List<T>>>爲您選擇的TKeyT

最簡單的方法將使用一個List<T>如下:

List<IDictionary<TKey, List<T>>> dictionaries 
    = new List<IDictionary<TKey, List<T>>>(); 

dictionaries.Add(dictionary1); // Your variable 
dictionaries.Add(dictionary2); // Your variable 

// Add any other dictionaries here. 

// Code as above! 
+0

如何填充'詞典'? – 2010-01-25 14:57:32

+0

更新了我的答案,包括如何填充「詞典」變量。 – 2010-01-25 17:25:37

1

您只需要將解決方案中的項目合併部分更改爲上一個問題。 對於對象,我們有這樣的:

.ToDictionary(group => group.Key, group => group.First()) 

即對於重複項目,只需佔據第一位。

但我們可以用這個:

.ToDictionary(group => group.Key, group => group.SelectMany(list => list).ToList()); 

來連接列表。

所以,最終的表現將是

var result = dictionaries.SelectMany(dict => dict) 
      .ToLookup(pair => pair.Key, pair => pair.Value) 
      .ToDictionary(group => group.Key, 
          group => group.SelectMany(list => list).ToList()); 

你可以嘗試不同的合併表達,如果你需要一些額外的列表組合邏輯(例如,只合並不同的項目)

1

我會是第一個承認這不是那麼漂亮,但這對我很有用。

var d1 = new Dictionary<string, List<string>>(); 
var d2 = new Dictionary<string, List<string>>(); 

d1["test"] = new List<string>() { "Stockholm", "Motala" }; 
d1["more"] = new List<string>() { "numerous", "populous", "bigger", "plentiful" }; 
d2["test"] = new List<string>() { "Washington", "Charlottesville" }; 
d2["less"] = new List<string>() { "insufficient", "small", "imperceptible" }; 

var intersect = (from key in d1.Keys.Intersect(d2.Keys) select new { Key = key, Value = new List<string>(d1[key].Concat(d2[key])) }).ToDictionary(d => d.Key, d => d.Value); 
var merged = d1.Concat(d2).Where(d => !intersect.Keys.Contains(d.Key)).Concat(intersect).ToDictionary(d => d.Key, d => d.Value);