在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);
关于重复键的处理:在发生冲突的情况下,保存到字典中的值并不重要,只要它是一致的。
如果有多个键(“右”键取代“左”键),这不会爆炸,可以合并一些字典(如果需要),并保留类型(限制它需要一个有意义的默认公共构造函数):
public static class DictionaryExtensions
{
// Works in C#3/VS2008:
// 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;
}
}
与之前没有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;
}
}
选项1:这取决于在确定两个字典中都没有重复键的情况下想要发生什么。比你能做的:
var result = dictionary1.Union(dictionary2).ToDictionary(k => k.Key, v => v.Value)
注意:如果在字典中获得任何重复的键,将抛出错误。
选项2:如果你可以有重复的键,那么你必须使用where子句来处理重复的键。
var result = dictionary1.Union(dictionary2.Where(k => !dictionary1.ContainsKey(k.Key))).ToDictionary(k => k.Key, v => v.Value)
注意:它不会得到重复的密钥。如果有任何重复的键,那么它将获得dictionary1的键。
选项3:如果你想使用ToLookup。然后您将得到一个查找,每个键可以有多个值。你可以把这个查找转换成一个字典:
var result = dictionaries.SelectMany(dict => dict)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
using System.Collections.Generic;
using System.Linq;
public static class DictionaryExtensions
{
public enum MergeKind { SkipDuplicates, OverwriteDuplicates }
public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, MergeKind kind = MergeKind.SkipDuplicates) =>
source.ToList().ForEach(_ => { if (kind == MergeKind.OverwriteDuplicates || !target.ContainsKey(_.Key)) target[_.Key] = _.Value; });
}
你可以跳过/忽略(默认)或覆盖副本:如果你对Linq性能不过分挑剔,而是像我一样喜欢简洁的可维护代码:在这种情况下,你可以删除默认的MergeKind。skipduplicate用于强制调用者进行选择,并使开发人员知道结果将是什么!