在c#中合并2个或更多字典(Dictionary<TKey, TValue>)的最佳方法是什么?
(像LINQ这样的3.0特性就可以了)。
我正在考虑一个方法签名,如下所示:
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);
关于重复键的处理:在发生冲突的情况下,保存到字典中的值并不重要,只要它是一致的。
使用扩展方法合并。当存在重复的键时,它不会抛出异常,而是用第二个字典中的键替换这些键。
internal static class DictionaryExtensions
{
public static Dictionary<T1, T2> Merge<T1, T2>(this Dictionary<T1, T2> first, Dictionary<T1, T2> second)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
var merged = new Dictionary<T1, T2>();
first.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
second.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
return merged;
}
}
用法:
Dictionary<string, string> merged = first.Merge(second);
与之前没有LINQ的情况下再次简化,如果存在则使用bool默认值非破坏性合并,如果为true则完全覆盖,而不是使用enum。它仍然适合我自己的需要,而不需要任何花哨的代码:
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)
{
foreach (KeyValuePair _ in source)
if (overwrite || !target.ContainsKey(_.Key))
target[_.Key] = _.Value;
}
}
@Tim:应该是注释,但是注释不允许代码编辑。
Dictionary<string, string> t1 = new Dictionary<string, string>();
t1.Add("a", "aaa");
Dictionary<string, string> t2 = new Dictionary<string, string>();
t2.Add("b", "bee");
Dictionary<string, string> t3 = new Dictionary<string, string>();
t3.Add("c", "cee");
t3.Add("d", "dee");
t3.Add("b", "bee");
Dictionary<string, string> merged = t1.MergeLeft(t2, t2, t3);
注意:我应用了@ANeves对@Andrew Orsich的解决方案的修改,所以mergleft现在看起来像这样:
public static Dictionary<K, V> MergeLeft<K, V>(this Dictionary<K, V> me, params IDictionary<K, V>[] others)
{
var newMap = new Dictionary<K, V>(me, me.Comparer);
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;
}
这在一定程度上取决于如果遇到重复项,你希望发生什么。例如,你可以这样做:
var result = dictionaries.SelectMany(dict => dict)
.ToDictionary(pair => pair.Key, pair => pair.Value);
如果您获得任何重复的键,将抛出异常。
编辑:如果你使用ToLookup,那么你会得到一个查找,每个键可以有多个值。然后你可以把它转换成一个字典:
var result = dictionaries.SelectMany(dict => dict)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
这有点难看——而且效率很低——但从代码的角度来说,这是最快的方法。(不得不承认,我还没有测试过它。)
当然,您也可以编写自己的ToDictionary2扩展方法(有一个更好的名字,但我现在没有时间去想)——这并不难做到,只是覆盖(或忽略)重复的键。重要的一点(在我看来)是使用SelectMany,并意识到字典支持键/值对的迭代。