Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine two Dictionaries with LINQ

My question has been flagged as a possible duplicate of this question: How to combine two dictionaries without looping?

I believe my question is different because I am asking how to combine two dictionaries in a particular way: I want all items from Dictionary1 plus all items from Dictionary2 that are not in (ie the key does not exist) in Dictionary1.

I have two dictionaries like this:

var d1 = new Dictionary<string,object>();
var d2 = new Dictionary<string,object>();

d1["a"] = 1;
d1["b"] = 2;
d1["c"] = 3;

d2["a"] = 11;
d2["e"] = 12;
d2["c"] = 13;

I would like to combine them into a new Dictionary (technically, it does not have to be a dictionary, it could just be a sequence of KeyValuePairs) such that the output contains all of the KeyValuePairs from d1 and only the KeyValuePairs from d2 whose Key does not appear in d1.

Conceptually:

var d3 = d1.Concat(d2.Except(d1))

But that is giving me all of the elements from d1 and d2.

Seems like it should be obvious, but I must be missing something.

like image 268
wageoghe Avatar asked Feb 04 '11 23:02

wageoghe


People also ask

Can we merge two dictionaries in C#?

We can use LINQ to merge any number of dictionaries in C#. The following code example shows how to implement this. If any of the keys gets repeated in any dictionary, the above code throws an ArgumentException. To handle this, the idea is to group elements based on the key before converting it to a dictionary.


4 Answers

When you use Except by default it uses the default equality comparer, which for the KeyValuePair type compares both the keys and the values. You could this approach instead:

var d3 = d1.Concat(d2.Where(kvp => !d1.ContainsKey(kvp.Key))); 
like image 176
Mark Byers Avatar answered Oct 05 '22 00:10

Mark Byers


var d3 = d1.Concat(d2.Where(kvp => ! d1.ContainsKey(kvp.Key)))            .ToDictionary(x => x.Key, x => x.Value); 

This is working for me.

like image 41
SP007 Avatar answered Oct 04 '22 23:10

SP007


Well I don't know if it's a new feature in LinQ, but that's exactly what .Union() does :

var d3 = d1.Union(d2);

Of course with Dictionaries you'll have to give a custom equality comparer to match only the keys :

class KeyValuePairComparer<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>>
{
    public bool Equals(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y)
    {
        return x.Key.Equals(y.Key);
    }
    public int GetHashCode(KeyValuePair<TKey, TValue> x)
    {
        return x.GetHashCode();
    }
}

and then :

var d3 = d1.Union(d2, new KeyValuePairComparer<string, object>());

With your example, the output would be (tested in C# interactive) :

> d1.Union(d2, new KeyValuePairComparer<string, object>())
UnionIterator { { "a", 1 }, { "b", 2 }, { "c", 3 }, { "e", 12 } }

Note the difference :

> d2.Union(d1, new KeyValuePairComparer<string, object>())
UnionIterator { { "a", 11 }, { "e", 12 }, { "c", 13 }, { "b", 2 } }
like image 38
Thibault Lemaire Avatar answered Oct 05 '22 01:10

Thibault Lemaire


Jon Skeet (as usual) has an extension method allowing you to do this: Can I specify my explicit type comparator inline?

like image 34
DaveShaw Avatar answered Oct 04 '22 23:10

DaveShaw