2010-10-25 187 views
17

我正在C#中开发一个以.NET 3.5为目标的应用程序。其中,我有2个类似的字典,其中包含我的应用中特定元素集的验证条件。两本字典都有相同的签名。第一个词典具有默认设置,第二个词典包含一些用户定义的设置。C#合并2个字典

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

我想将2个字典组合成一个包含两个字典元素的字典。

我遇到的问题是两个字典都可能有一些相同的键值。我想要的基本规则是将这两个词典组合起来,并且如果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循环的行为我在用?

回答

38

两点:

  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是要帮助你所有的东西。

+3

+1点#2(http://msdn.microsoft.com/en-us/library/k7z0zy8k.aspx) – Brad 2010-10-25 13:59:12

+1

+1教我的东西我不知道。我相对自我教育与C#,并不知道你可以用字典做到这一点。 – psubsee2003 2010-10-25 14:09:54

+0

@ psubsee2003:欢呼声。我很确定我使用了与您的代码相同的模式,然后才发现它是多余的(偶然,正在用反射器探索)。 – Ani 2010-10-25 14:17:17

2

如果你要这个做了很多,那么我建议你写字典键的相等比较:

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

这一个为我工作,即使没有'KeyEqualityComparer'类/参数。谢谢 – AceMark 2012-03-15 03:28:26

1

我认为我最初选择的答案仍然是这个特定情况下的最佳答案,我发现自己处于另一个类似的情况,前一段时间我想将对象转换为字典并合并到一起类似的方式,所以我想我会在这里添加该解决方案来帮助未来的人。我找到了一种新方法,而不是将它们转换为字典并在所选答案中使用该方法。

我实际上在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<>键(这种情况下Name

如果您不能覆盖FooEquals(),那么另一种选择是使用Union()

GroupBy()代替
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; 
} 
8

这是一个很好的基于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; 
     } 
    } 
} 
+1

我会更改字典在IDictionary – 2015-08-23 11:24:39

+0

如果这是一个字典集合'IEnumerable <字典>'如何能匹配它们? – barteloma 2017-08-15 13:09:49