【问题标题】:Merging Dictionary containing a List in C#在 C# 中合并包含列表的字典
【发布时间】:2011-01-09 02:34:34
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: c# linq dictionary merge


    【解决方案1】:

    我建议创建自己的扩展方法。修改起来会更高效、更容易。

    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);
            }
        }
    

    【讨论】:

      【解决方案2】:

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

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

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

      结果集包含组,其中每个组的键是原始字典中的键,组的内容是具有相同键的列表的IEnumerable&lt;List&lt;T&gt;&gt;。从这些组中,我们可以使用带有SelectManySelect 转换将所有List&lt;T&gt; 合并为一个IEnumerable&lt;T&gt;

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

      然后我们可以使用ToDictionary 转换从中获取字典,将IEnumerable&lt;T&gt; 转换回List&lt;T&gt;

      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。我假设它是一种为您选择的TKeyT 实现IEnumerable&lt;IDictionary&lt;TKey, List&lt;T&gt;&gt;&gt; 的类型。

      最简单的方法是使用List&lt;T&gt;,如下所示:

      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!
      

      【讨论】:

      • 你如何填充dictionaries
      • 更新了我的答案,包括如何填充 dictionaries 变量。
      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        我会第一个承认这不是那么漂亮,但这对我有用。

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

        【讨论】:

          猜你喜欢
          • 2019-06-20
          • 1970-01-01
          • 1970-01-01
          • 2018-09-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-07
          相关资源
          最近更新 更多