2010-01-25 59 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);