28

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.

Community
  • 1
  • 1
wageoghe
  • 27,390
  • 13
  • 88
  • 116
  • Possible duplicate of [How to add 2 Dictionary contents without looping in c#](http://stackoverflow.com/questions/712927/how-to-add-2-dictionary-contents-without-looping-in-c-sharp) – Michael Freidgeim Apr 28 '16 at 05:01
  • 1
    This question is not a duplicate of that question. This question is asking how to combine two dictionaries, d1 and d2, such that the resulting dictionary has all items from d1 plus all items from d2 that are not already in d1. The other question is asking, and the answers explain how, to combine two dictionaries without any extra conditions. – wageoghe Apr 28 '16 at 17:58

6 Answers6

41

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)));
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
14
var d3 = d1.Concat(d2.Where(kvp => ! d1.ContainsKey(kvp.Key)))
           .ToDictionary(x => x.Key, x => x.Value);

This is working for me.

Yuck
  • 49,664
  • 13
  • 105
  • 135
SP007
  • 1,871
  • 1
  • 22
  • 31
7

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 } }
2

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

Community
  • 1
  • 1
DaveShaw
  • 52,123
  • 16
  • 112
  • 141
  • That is really cool! I probably won't do that now, but will file it away for later. – wageoghe Feb 04 '11 at 23:25
  • There are loads and loads of Linq extensions. I would have used ExceptBy in this case come to think of it : http://code.google.com/p/morelinq/source/browse/trunk/MoreLinq/ExceptBy.cs – DaveShaw Feb 04 '11 at 23:29
1

Another solution using your own IEqualityComparer like in the answer of @bitxwise and @DaveShaw, but not using Except() which makes it a little simpler:

var d3 = d1.Concat(d2).Distinct(new MyComparer());
BSharp
  • 917
  • 8
  • 12
1

You can also use your own IEqualityComparer. Example below:

public class MyComparer : IEqualityComparer<KeyValuePair<string,string>> {
    public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y) {
        return x.Key.Equals(y.Key);
    }

    public int GetHashCode(KeyValuePair<string, string> obj) {
        return obj.Key.GetHashCode();
    }
}

...

Dictionary<string, string> d1 = new Dictionary<string, string>();
d1.Add("A", "B");
d1.Add("C", "D");

Dictionary<string, string> d2 = new Dictionary<string, string>();
d2.Add("E", "F");
d2.Add("A", "D");
d2.Add("G", "H");

MyComparer comparer = new MyComparer();

var d3 = d1.Concat(d2.Except(d1, comparer));
foreach (var a in d3) {
    Console.WriteLine("{0}: {1}", a.Key, a.Value);
}
bitxwise
  • 3,534
  • 2
  • 17
  • 22
  • Thanks! That's a pretty cool approach as well as Mark's. I saw that I could use an IEqualityComparer, but I wondered if there was an even easier way. – wageoghe Feb 04 '11 at 23:21
  • Normally for quick things like KEY comparisons, LINQ is probably the way to go, but if you need something a bit more complicated (and need to reuse it), the `IEqualityComparer` could be better, especially if implemented as an extension method. – bitxwise Feb 04 '11 at 23:30