开发者

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.


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


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

This is working for me.


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


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


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


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜