【问题标题】:Merge Dictionary<TKey, TValue> with Enumerable.Union method使用 Enumerable.Union 方法合并 Dictionary<TKey, TValue>
【发布时间】:2011-06-15 02:00:56
【问题描述】:

我正在测试 UNION 方法以合并到字典(字典类型)。它适用于 TValue 类型是 string 或 int 甚至是 object。但如果 TValue 类型是一个集合(使用 List 和 object[] 测试),则会引发异常:"ArgumentException: An item with the same key has been added."

这是我的代码:

Dictionary<int,string> _dico1 = new Dictionary<int, string>()
{
    {0, "zero"},
    {1, "one"}
};

Dictionary<int,string> _dico2 = new Dictionary<int,string>()
{
    {1 , "one"},
    {2 , "two"},
    {3 , "three"},
    {4 , "four"},
    {5 , "five"},
    {6 , "six"}
};

Dictionary<int, List<string>> _dico3 = new Dictionary<int, List<string>>()
{
    {0, new List<string>{"zero"}},
    {1, new List<string>{"one"}}
};

Dictionary<int, List<string>> _dico4 = new Dictionary<int, List<string>>()
{
    {1, new List<string>{"one"}},
    {2, new List<string>{"two"}},
    {3, new List<string>{"three"}},
    {4, new List<string>{"four"}},
    {5, new List<string>{"five"}},
    {6, new List<string>{"six"}},
};

    // works fine
    var mergeDico = _dico1.Union(_dico2).ToDictionary(key => key.Key, value => value.Value);

    // throw an ArgumentException : An item with the same key has already been added
    var mergeDico2 = _dico3.Union(_dico4).ToDictionary(key => key.Key, value => value.Value);

为什么行为不一样?以及如何解决这个问题?

谢谢!

【问题讨论】:

    标签: c# dictionary merge union


    【解决方案1】:

    在第一种情况下,Union 丢弃了重复的键,因为键/值对本身是相等的。在第二种情况下,它们不是,因为 List&lt;String&gt;{"one"} 不等于另一个 List&lt;string&gt;{"one"}

    我怀疑您希望您的 Union 调用使用 IEqualityComparer,它只考虑字典中的键。

    【讨论】:

    • 所以,在第一种情况下,它可以工作,因为 TValue 类型是值类型(int)或不可变类型(字符串),而在第二种情况下,它会失败,因为 TValue 是引用类型?
    • @Florian:这不是直接由于不变性——而是因为所涉及的类型是否具有值相等语义。您仍然可以拥有不这样做的不可变类型。
    【解决方案2】:

    您可以将第二对字典与如下代码合并:

    var mergeDico2 = _dico3 .Concat(_dico4) .GroupBy(_=> _.Key, _ => _.Value) .ToDictionary( group => group.Key, group => group.SelectMany(_ => _).ToList());

    它将生成一个新字典,其中每个值都是连接两个字典值的列表的结果。如果您只需要列表的不同元素,您可以将 ToDictionary 调用更改为此:

    var mergeDico2 = _dico3 .Concat(_dico4) .GroupBy(_=> _.Key, _ => _.Value) .ToDictionary( group => group.Key, group => group.SelectMany(_ => _).Distinct().ToList());

    【讨论】:

      【解决方案3】:

      正如 Jon 已经提到的,我们需要实现 IEqualityComparer 来解决上述问题。这是如何完成的代码:

      IEqualityComparer:

      public class MyEqualityComparer : IEqualityComparer<KeyValuePair<int,List<string>>>
      {
          public bool Equals(KeyValuePair<int, List<string>> x, KeyValuePair<int, List<string>> y)
          {
              //Let's say we are comparing the keys only.
              return x.Key == y.Key;
          }
      
          public int GetHashCode(KeyValuePair<int, List<string>> obj)
          {
              return obj.Key.GetHashCode();
          }
      }
      

      用法:

      Dictionary<int, List<string>> _dico3 = new Dictionary<int, List<string>>()
          {
              {0, new List<string> {"zero"}},
              {1, new List<string> {"one"}}
          };
      
      Dictionary<int, List<string>> _dico4 = new Dictionary<int, List<string>>()
          {
              {1, new List<string> {"one"}},
              {2, new List<string> {"two"}},
              {3, new List<string> {"three"}},
              {4, new List<string> {"four"}},
              {5, new List<string> {"five"}},
              {6, new List<string> {"six"}},
          };
      
      Dictionary<int, List<string>> mergeDico2 = _dico3.Union(_dico4, new MyEqualityComparer())
          .ToDictionary(x => x.Key, x => x.Value);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-14
        • 2011-04-11
        • 2011-01-16
        • 1970-01-01
        相关资源
        最近更新 更多