代码之家  ›  专栏  ›  技术社区  ›  Fiona - myaccessible.website

合并包含C中列表的字典#

  •  3
  • Fiona - myaccessible.website  · 技术社区  · 15 年前

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

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

    我正在寻找合并 Dictionary<Object1, List<Object2>>, 有以下要求:

    • 如果字典1包含与字典2相同的键,则它们的 List<Object2> 应组合列表。最后,您将得到一个新的键值对,其中包含共享键和两个字典中的组合列表。
    • 如果字典1包含字典2不包含的键,则 列出<对象2> 字典1中的列表应成为值,反之亦然。

    这在LINQ中可能是不可能的,或者值得用for循环之类的语言编写它,但是最好有一个优雅的解决方案。

    4 回复  |  直到 7 年前
        1
  •  3
  •   Keith Rousseau    15 年前

    我建议您创建自己的扩展方法。它将更有效,更容易修改。

    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
  •  2
  •   Paul Turner    15 年前

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

    如果我们首先使用 SelectMany ,我们可以按元素的键将它们组合在一起。

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

    结果集包含组,其中每个组的键是原始字典中的键,组的内容是 IEnumerable<List<T>> 具有相同键的列表。从这些组中,我们可以合并所有 List<T> 成单一 IEnumerable<T> 使用A Select 转换 选择许多 .

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

    然后我们可以使用 ToDictionary 转换,转换 IEnumerable<t> 回到A 列表& 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 你的选择。

    最简单的方法是使用 列表& 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!
    
        3
  •  2
  •   psubsee2003 Miki Shah    7 年前

    您只需要更改项目合并部分即可解决上一个问题。 对于对象,我们有:

    .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
  •  1
  •   Jonas Elfström    15 年前

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

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