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 !
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.
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());
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With