在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);
关于重复键的处理:在发生冲突的情况下,保存到字典中的值并不重要,只要它是一致的。
基于上面的答案,但添加一个Func-parameter,让调用者处理重复:
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));
}
这个聚会现在几乎已经死了,但是user166390的“改进”版本已经进入了我的扩展库。
除了一些细节之外,我还添加了一个委托来计算合并的值。
/// <summary>
/// Merges a dictionary against an array of other dictionaries.
/// </summary>
/// <typeparam name="TResult">The type of the resulting dictionary.</typeparam>
/// <typeparam name="TKey">The type of the key in the resulting dictionary.</typeparam>
/// <typeparam name="TValue">The type of the value in the resulting dictionary.</typeparam>
/// <param name="source">The source dictionary.</param>
/// <param name="mergeBehavior">A delegate returning the merged value. (Parameters in order: The current key, The current value, The previous value)</param>
/// <param name="mergers">Dictionaries to merge against.</param>
/// <returns>The merged dictionary.</returns>
public static TResult MergeLeft<TResult, TKey, TValue>(
this TResult source,
Func<TKey, TValue, TValue, TValue> mergeBehavior,
params IDictionary<TKey, TValue>[] mergers)
where TResult : IDictionary<TKey, TValue>, new()
{
var result = new TResult();
var sources = new List<IDictionary<TKey, TValue>> { source }
.Concat(mergers);
foreach (var kv in sources.SelectMany(src => src))
{
TValue previousValue;
result.TryGetValue(kv.Key, out previousValue);
result[kv.Key] = mergeBehavior(kv.Key, kv.Value, previousValue);
}
return result;
}
下面的方法对我有用。如果存在重复项,则使用dictA的值。
public static IDictionary<TKey, TValue> Merge<TKey, TValue>(this IDictionary<TKey, TValue> dictA, IDictionary<TKey, TValue> dictB)
where TValue : class
{
return dictA.Keys.Union(dictB.Keys).ToDictionary(k => k, k => dictA.ContainsKey(k) ? dictA[k] : dictB[k]);
}
使用扩展方法合并。当存在重复的键时,它不会抛出异常,而是用第二个字典中的键替换这些键。
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);
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用于强制调用者进行选择,并使开发人员知道结果将是什么!
试试
namespace Extensions
{
public static class DictionaryExtensions
{
public static Dictionary<T, Y> MergeWith<T, Y>(this Dictionary<T, Y> dictA,
Dictionary<T, Y> dictB)
{
foreach (var item in dictB)
{
if (dictA.ContainsKey(item.Key))
dictA[item.Key] = item.Value;
else
dictA.Add(item.Key, item.Value);
}
return dictA;
}
}
}
当你想合并两个字典时
var d1 = new Dictionary<string, string>();
var d2 = new Dictionary<string, string>();
d1.MergeWith(d2);