7

I'm testing the UNION method to merge to dictionaries (of type Dictionary). It works fine with TValue type is string or int or even object. But if TValue type is a collection (tested with List and object[]) an exception is thrown : "ArgumentException: An item with the same key has already been added."

Here is my code :

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

Why the behavior is not the same ? And How to resolve this problem ?

Thank you !

Florian
  • 4,507
  • 10
  • 53
  • 73

3 Answers3

7

In the first case, the Union is discarding the duplicate keys because the key/value pairs themselves are equal. In the second case they're not, because a List<String>{"one"} isn't equal to another List<string>{"one"}.

I suspect you want your Union call to use an IEqualityComparer which only takes account of the keys within the dictionary.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • So, in the first case, it works because TValue type are value types (int) or immutable type (string) and, in second case it fails because TValue is reference type ? – Florian Jan 25 '11 at 09:52
  • 5
    @Florian: It's not directly due to immutability - it's due to whether the types involved have value equality semantics. You can still have immutable types which don't do that. – Jon Skeet Jan 25 '11 at 09:52
2

You can merge second pair of dictionaries with code like this:


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

It will produce a new dictionary where each value is a result of concatenating lists from values of both dictionaries. If you need only distinct elements of lists you can change ToDictionary call to this one:


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

Konstantin Oznobihin
  • 5,234
  • 24
  • 31
1

As Jon mentioned already that we need to implement IEqualityComparer to resolve the above issue. Here is the code how it can be done:

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

Usage:

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);
Raghav
  • 8,772
  • 6
  • 82
  • 106