在 C# 中合并字典
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/294138/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Merging dictionaries in C#
提问by orip
What's the best way to merge 2 or more dictionaries (Dictionary<T1,T2>
) in C#?
(3.0 features like LINQ are fine).
Dictionary<T1,T2>
在 C# 中合并 2 个或更多词典 ( )的最佳方法是什么?(像 LINQ 这样的 3.0 功能很好)。
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.
编辑:从 JaredPar 和 Jon Skeet 那里得到了一个很酷的解决方案,但我正在考虑处理重复键的东西。在发生冲突的情况下,只要保持一致,将哪个值保存到 dict 并不重要。
采纳答案by Jon Skeet
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 couldthen convert that to a dictionary:
编辑:如果你使用 ToLookup 那么你会得到一个查找,每个键可以有多个值。然后,您可以将其转换为字典:
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.
您当然可以编写自己的 ToDictionary2 扩展方法(使用更好的名称,但我现在没有时间去想)——这并不难,只需覆盖(或忽略)重复键即可。重要的一点(在我看来)是使用 SelectMany,并意识到字典支持对其键/值对进行迭代。
回答by orip
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;
}
回答by JaredPar
Try the following
尝试以下
static Dictionary<TKey, TValue>
Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> enumerable)
{
return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value);
}
回答by Bryan Watts
How about adding a params
overload?
添加一个params
重载怎么样?
Also, you should type them as IDictionary
for maximum flexibility.
此外,您应该输入它们以IDictionary
获得最大的灵活性。
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);
}
回答by Andrew Harry
Here is a helper function I use:
这是我使用的辅助函数:
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 || first == null) return;
foreach (var item in second)
if (!first.ContainsKey(item.Key))
first.Add(item.Key, item.Value);
}
}
}
回答by ctrlalt313373
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
回答by ctrlalt313373
This 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/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;
}
}
回答by Jonas Stensved
I would do it like this:
我会这样做:
dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value));
Simple and easy. According to this blog postit's even faster than most loops as its underlying implementation accesses elements by index rather than enumerator (see this answer).
简单易行。根据这篇博客文章,它甚至比大多数循环都要快,因为它的底层实现通过索引而不是枚举器访问元素(请参阅此答案)。
It will of course throw an exception if there are duplicates, so you'll have to check before merging.
如果有重复项,它当然会抛出异常,因此您必须在合并之前进行检查。
回答by toong
Based on the answers above, but adding a Func-parameter to let the caller handle the duplicates:
基于上面的答案,但添加一个 Func 参数让调用者处理重复项:
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));
}
回答by gxtaillon
The party's pretty much dead now, but here's an "improved" version of user166390 that made its way into my extension library. Apart from some details, I added a delegate to calculate the merged value.
该派对现在几乎已经死了,但这里有一个“改进”版本的 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;
}