What\'s the best way to merge 2 or more dictionaries (Dictionary
) in C#?
(3.0 features like LINQ are fine).
I\'m thinking of a method signa
I know this is an old question, but since we now have LINQ you can do it in a single line like this
Dictionary<T1,T2> merged;
Dictionary<T1,T2> mergee;
mergee.ToList().ForEach(kvp => merged.Add(kvp.Key, kvp.Value));
or
mergee.ToList().ForEach(kvp => merged.Append(kvp));
or :
public static IDictionary<TKey, TValue> Merge<TKey, TValue>( IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
{
return x
.Except(x.Join(y, z => z.Key, z => z.Key, (a, b) => a))
.Concat(y)
.ToDictionary(z => z.Key, z => z.Value);
}
the result is a union where for duplicate entries "y" wins.
Based on the answers above, but adding a Func-parameter to let the caller handle the duplicates:
public static Dictionary<TKey, TValue> Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> dicts,
Func<IGrouping<TKey, TValue>, TValue> resolveDuplicates)
{
if (resolveDuplicates == null)
resolveDuplicates = new Func<IGrouping<TKey, TValue>, TValue>(group => group.First());
return dicts.SelectMany<Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>>(dict => dict)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => resolveDuplicates(group));
}
Simplified from use compared with my earlier answer with a bool default of non-destructive merge if existing or overwrite entirely if true rather than using an enum. It still suits my own needs without any fancier code ever being required:
using System.Collections.Generic;
using System.Linq;
public static partial class Extensions
{
public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, bool overwrite = false)
{
source.ToList().ForEach(_ => {
if ((!target.ContainsKey(_.Key)) || overwrite)
target[_.Key] = _.Value;
});
}
}
Note that if you use an extension method called 'Add', you get to use collection initializers to combine as many dictionaries as needed like this:
public static void Add<K, V>(this Dictionary<K, V> d, Dictionary<K, V> other) {
foreach (var kvp in other)
{
if (!d.ContainsKey(kvp.Key))
{
d.Add(kvp.Key, kvp.Value);
}
}
}
var s0 = new Dictionary<string, string> {
{ "A", "X"}
};
var s1 = new Dictionary<string, string> {
{ "A", "X" },
{ "B", "Y" }
};
// Combine as many dictionaries and key pairs as needed
var a = new Dictionary<string, string> {
s0, s1, s0, s1, s1, { "C", "Z" }
};
Merging using an EqualityComparer
that maps items for comparison to a different value/type. Here we will map from KeyValuePair
(item type when enumerating a dictionary) to Key
.
public class MappedEqualityComparer<T,U> : EqualityComparer<T>
{
Func<T,U> _map;
public MappedEqualityComparer(Func<T,U> map)
{
_map = map;
}
public override bool Equals(T x, T y)
{
return EqualityComparer<U>.Default.Equals(_map(x), _map(y));
}
public override int GetHashCode(T obj)
{
return _map(obj).GetHashCode();
}
}
Usage:
// if dictA and dictB are of type Dictionary<int,string>
var dict = dictA.Concat(dictB)
.Distinct(new MappedEqualityComparer<KeyValuePair<int,string>,int>(item => item.Key))
.ToDictionary(item => item.Key, item=> item.Value);