在C#中合并包含List的字典

时间:2010-01-25 13:32:17

标签: c# linq dictionary merge

这与this question有关,关于如何在C#中合并两个词典。提出了一个优雅的Linq解决方案,很酷。

但是,这个问题与Dictionary<Object1, Object2>,有关,而我有一个字典,其值为List<Object2>.

我正在寻找合并Dictionary<Object1, List<Object2>>,以满足以下要求的解决方案:

  • 如果Dictionary1包含与Dictionary2相同的键,则应合并其List<Object2>列表。您最终会得到一个带有共享密钥的新键值对,以及来自两个词典的组合列表。
  • 如果Dictionary1包含Dictionary2没有的键,则Dictionary1中的List<Object2>列表应该成为值,反之亦然。

这在Linq中可能是不可能的,或者用for循环等手写它可能是值得的,但是有一个优雅的解决方案会很好。

4 个答案:

答案 0 :(得分: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 :(得分:2)

困难在于处理关键冲突的合并。

如果我们首先使用SelectMany展平所有输入词典,我们可以按键将元素组合在一起。

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

结果集包含组,其中每个组的键是原始词典中的键,组的内容是具有相同键的列表的IEnumerable<List<T>>。在这些群组中,我们可以使用List<T>转换IEnumerable<T>将所有Select合并为一个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>>>TKey实现T的类型。

最简单的方法是使用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!

答案 2 :(得分:2)

您只需将解决方案中的项目合并部分更改为上一个问题。 对于对象,我们有:

.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());

如果您需要一些额外的列表合并逻辑(例如,只合并不同的项目),您可以尝试使用不同的合并表达式

答案 3 :(得分: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);