views:

7955

answers:

8

What's the best way to merge 2 or more dictionaries (Dictionary<T1,T2>) in C#? (3.0 features like LINQ are fine).

I'm thinking of a method signature along the lines of:

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);

or

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);

EDIT: Got a cool solution from JaredPar and Jon Skeet, but I was thinking of something that handles duplicate keys. In case of collision, it doesn't matter which value is saved to the dict as long as it's consistent.

+7  A: 

The trivial solution would be:

using System.Collections.Generic;
...
public static Dictionary<TKey, TValue>
    Merge<TKey,TValue>(IEnumerable<Dictionary<TKey, TValue>> dictionaries)
{
    var result = new Dictionary<TKey, TValue>();
    foreach (var dict in dictionaries)
        foreach (var x in dict)
            result[x.Key] = x.Value;
    return result;
}
orip
+5  A: 

Try the following

static Dictionary<TKey, TValue>
    Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> enumerable)
{
    enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value);
}
JaredPar
Thanks JaredPar, cool!
orip
it should actually be: return enumerable.SelectMany...
Rami A.
+13  A: 

This partly depends on what you want to happen if you run into duplicates. For instance, you could do:

var result = dictionaries.SelectMany(dict => dict)
                         .ToDictionary(pair => pair.Key, pair => pair.Value);

That will blow up if you get any duplicate keys.

EDIT: If you use ToLookup then you'll get a lookup which can have multiple values per key. You could then convert that to a dictionary:

var result = dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value);
                         .ToDictionary(group => group.Key, group => group.First());

It's a bit ugly - and inefficient - but it's the quickest way to do it in terms of code. (I haven't tested it, admittedly.)

You could write your own ToDictionary2 extension method of course (with a better name, but I don't have time to think of one now) - it's not terribly hard to do, just overwriting (or ignoring) duplicate keys. The important bit (to my mind) is using SelectMany, and realising that a dictionary supports iteration over its key/value pairs.

Jon Skeet
Thanks Jon, you're right, I should have specified how duplicates should be handled (just did). Cool solution!
orip
+1  A: 

How about adding a params overload?

Also, you should type them as IDictionary for maximum flexibility.

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(IEnumerable<IDictionary<TKey, TValue>> dictionaries)
{
    // ...
}

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(params IDictionary<TKey, TValue>[] dictionaries)
{
    return Merge((IEnumerable<TKey, TValue>) dictionaries);
}
Bryan Watts
Nice interface :)
orip
Downvoted because this doesn’t answer the question at all.
Timwi
+2  A: 

Hey All here is my answer:

using System.Collections.Generic;
namespace HelperMethods
{
    public static class MergeDictionaries
    {
        public static void Merge<TKey, TValue>(this IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second)
        {
            if (second == null) return;
            if (first == null) first = new Dictionary<TKey, TValue>();
            foreach (var item in second) 
                if (!first.ContainsKey(item.Key)) 
                    first.Add(item.Key, item.Value);
        }
    }
}
Harry
+1  A: 
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
David Osborn
A: 

Is there a way to handle the duplicates too? i tried using the code suggested by David and I get 'An item with that key already exists'. The two dictionaries I am trying to merge have some duplicate keys, but if there are duplcicates, I want to merge the value. Is this possible?

Javid

Yes. Read all the answers.
pst
What is this? A question posted as an answer?
Timwi
+3  A: 

Well, I'm late to the party, but here is what I use. It doesn't explode if there are multiple keys ("righter" keys replace "lefter" keys), can merge a number of dictionaries (if desired) and preserves the type (with the restriction that it requires a meaningful default public constructor):

public static class DictionaryExtensions
{
    // Works in C#3/VS2080:
    // Returns a new dictionary of this ... others merged leftward.
    // Keeps the type of 'this', which must be default-instantiable.
    // Example: 
    //   result = map.MergeLeft(other1, other2, ...)
    public static T MergeLeft<T,K,V>(this T me, params IDictionary<K,V>[] others)
        where T : IDictionary<K,V>, new()
    {
        T newMap = new T();
        foreach (IDictionary<K,V> src in
            (new List<IDictionary<K,V>> { me }).Concat(others)) {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K,V> p in src) {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

}
pst
Well done. This was exactly what I needed. Nice use of generics. Agreed that the syntax is a bit awkward, but nothing you can do about it.
Peter M
Very nicely done!
Rohit Agarwal