【问题标题】:C# Merging 2 dictionariesC# 合并 2 个字典
【发布时间】:2010-10-25 13:50:53
【问题描述】:

我正在开发一个面向 .NET 3.5 的 C# 应用程序。在其中,我有 2 个类似的字典,其中包含我的应用程序中一组特定元素的验证标准。两个字典都有相同的签名。第一个字典具有默认设置,第二个字典包含一些用户定义的设置。

var default_settings = new Dictionary<string, MyElementSettings>();
var custom_settings = new Dictionary<string, MyElementSettings>();

我想将这两个字典合并为一个包含两个字典元素的字典。

我遇到的问题是两个字典有可能有一些相同的键值。我想要的基本规则是结合使用两个字典,如果 custom_settings 中的任何键已经存在于 default_settings 中,则 custom_settings 值将覆盖 default_settings 值。我最好的解决方案只是一个 foreach 循环,检查键是否存在于另一个字典中,如果不存在,则添加它。

foreach (var item in custom_settings)
{
    if (default_settings.ContainsKey(item.Key))
        default_settings[item.Key] = item.Value;
    else
        default_settings.Add(item.Key, item.Value);
}

我已经完成了一些基本的 LINQ 查询,但我仍在努力学习更高级的内容。我已经看到了一些将合并 2 个字典的查询,但大多数涉及使用重复键对任何元素进行分组,或者只返回一个只有重复键的集合/是否有 LINQ 查询或表达式可以模仿 foreach 循环的行为我在用吗?

【问题讨论】:

    标签: c# dictionary linq-to-objects


    【解决方案1】:

    两点:

    1. LINQ 不适合执行副作用。在这种情况下,您尝试改变现有集合而不是执行查询,因此我会回避纯 LINQ 解决方案。
    2. setter on the generic dictionary's indexer 已经具有在键不存在时添加键值对或在存在时覆盖值的效果。

    当你设置属性值时,如果 键在字典中,值与 该键被分配的 价值。如果钥匙不在 字典、键和 值被添加到字典中。

    所以你的foreach 循环基本上等同于:

    foreach (var item in custom_settings)
    {
       default_settings[item.Key] = item.Value;
    }
    

    现在已经很简洁了,所以我认为 LINQ 不会对您有太大帮助。

    【讨论】:

    • +1 教我一些我不知道的东西。我是相对自学 C# 的,从来不知道你可以用字典做到这一点。
    • @psubsee2003:干杯。我很确定在我发现它是多余的之前,我使用了与您的代码相同的模式(偶然地,正在使用反射器)。
    • 您也可以使用TryGetValue 方法来检查该对象是否已经存在于字典中。
    【解决方案2】:

    这是一个基于 Ani 回答的不错的扩展方法。

    public static class DictionaryExtensionMethods
    {
        public static void Merge<TKey, TValue>(this Dictionary<TKey, TValue> me, Dictionary<TKey, TValue> merge)
        {
            foreach (var item in merge)
            {
                me[item.Key] = item.Value;
            }
        }
    }
    

    【讨论】:

    • 我会在 IDictionary 上更改字典
    • 如果这是一个字典集合IEnumerable&lt;Dictionary&lt;TKey, TValu&gt;&gt; 如何匹配它们?
    【解决方案3】:

    如果您要经常这样做,那么我建议您为字典键编写一个相等比较器:

    private class KeyEqualityComparer<T, U> : IEqualityComparer<KeyValuePair<T, U>>
    {
        public bool Equals(KeyValuePair<T, U> x, KeyValuePair<T, U> y)
        {
            return x.Key.Equals(y.Key);
        }
         
        public int GetHashCode(KeyValuePair<T, U> obj)
        {
            return obj.Key.GetHashCode();
        }
    }
    

    然后每当您需要合并字典时,您可以执行以下操作

    var comparer = new KeyEqualityComparer<string, MyElementSettings>();
    dict1 = dict1.Union(dict2,comparer).ToDictionary(a => a.Key, b => b.Value);
    

    【讨论】:

    • 这个对我有用,即使没有 KeyEqualityComparer 类/参数。谢谢
    【解决方案4】:

    我认为我最初选择的答案仍然是这种特殊情况的最佳答案,不久前我发现自己处于另一个类似的情况,我有 2 个 IEnumerable&lt;&gt; 对象,我想将它们转换为字典并合并在一起以类似的方式,所以我想我会在此处添加该解决方案以帮助将来的某人。我没有将两者都转换为字典并使用所选答案中的方法,而是找到了一种新方法。

    我实际上在 SE-CodeReview 上发布了 initial solution 并且实际上提出了进一步完善它的建议。这是我使用的最终代码:

    public Dictionary<String, Foo> Merge(XElement element1, XElement element2)
    {
        IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML
        IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML
    
        var result = firstFoos.Union(secondFoos).ToDictionary(k=>k.Name, v=>v);
    
        return result;
    }
    
    public class Foo
    {
        public String Name { get; }
    
        // other Properties and Methods
        // .
        // .
        // .
    
        public override Boolean Equals(Object obj)
        {
            if (obj is Foo)
            {
                return this.Name == ((Foo)obj).Name;            
            }
    
            return false;
        }
    }
    

    这个的关键是Foo必须覆盖Equals()来定义哪些Foo对象可以被认为是重复的,并且定义哪些对象是重复的成员也应该是Dictionary&lt;&gt;键(在这种情况下@ 987654328@)

    如果您不能在Foo 中覆盖Equals(),那么另一种选择是使用Concat()GroupBy() 而不是Union()

    public Dictionary<String, Foo> Merge(XElement element1, XElement element2)
    {
        IEnumerable<Foo> firstFoos = GetXmlData(element1); // parse 1st set from XML
        IEnumerable<Foo> secondFoos = GetXmlData(element2); // parse 2nd set from XML
    
        var result = firstFoos.Concat(secondFoos)
                              .GroupBy(foo => foo.Name)
                              .Select(grp => grp.First())
                              .ToDictionary(k=>k.Name, v=>v);
    
        return result;
    }
    

    【讨论】:

      【解决方案5】:

      此版本创建一个新的完整字典,而不是修改一个字典并将当前字典与提供的字典合并。

      /// <summary>
      /// Merges the current dictionary with the supplied dictionary into a new dictionary.
      /// </summary>
      /// <typeparam name="TKey"></typeparam>
      /// <typeparam name="TValue"></typeparam>
      /// <param name="current"></param>
      /// <param name="keyValuePairs"></param>
      /// <returns></returns>
      public static IDictionary<TKey, TValue> Merge<TKey, TValue>(this IDictionary<TKey, TValue> current, IDictionary<TKey, TValue> keyValuePairs)
      {
          var dictionaryMerge = new Dictionary<TKey, TValue>(current);
          foreach (var item in keyValuePairs)
          {
              dictionaryMerge[item.Key] = item.Value;
          }
          return dictionaryMerge;
      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-19
        • 1970-01-01
        相关资源
        最近更新 更多