在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 21:58:46  来源:igfitidea点击:

Merging dictionaries in C#

c#dictionarymerge

提问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 paramsoverload?

添加一个params重载怎么样?

Also, you should type them as IDictionaryfor 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;
}