当前位置:  开发笔记 > 编程语言 > 正文

合并C#中的词典

如何解决《合并C#中的词典》经验,为你挑选了11个好方法。

Dictionary在C#中合并2个或更多字典()的最佳方法是什么?(像LINQ这样的3.0功能很好).

我正在考虑一种方法签名:

public static Dictionary
                 Merge(Dictionary[] dictionaries);

要么

public static Dictionary
                 Merge(IEnumerable> dictionaries);

编辑:从Jare​​dPar和Jon Skeet得到一个很酷的解决方案,但我正在考虑处理重复键的东西.在发生碰撞的情况下,只要它是一致的,将哪个值保存到dict并不重要.



1> Jon Skeet..:

这部分取决于你遇到重复的事情.例如,你可以这样做:

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,并意识到字典支持迭代其键/值对.


要实际合并值而不是仅从第一个字典中取出它们,您可以在Jon Skeet编辑的答案中将group => group.> first()替换为group => group.SelectMany(value => value).
现在我想知道GroupBy比ToLookup更适合吗?我认为ILookup只是添加了一个索引器,大小属性并且在IGrouping之上包含方法 - 所以它需要更快一点点?
@toong:说实话,要么应该没事.使用`GroupBy`确实可以更高效一点 - 但我怀疑它无论哪种方式都很重要.

2> Jonas Stensv..:

我会这样做:

dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value));

简单易行.根据这篇博客文章,它比大多数循环更快,因为它的底层实现通过索引而不是枚举器访问元素(参见本答案).

如果存在重复,它当然会抛出异常,因此您必须在合并之前进行检查.


如果重复很重要,请使用`dictionaryFrom.ToList().ForEach(x => dictionaryTo [x.Key] = x.Value)`.这样来自`dictionaryFrom`的值会覆盖可能存在的键的值.
这不可能比循环更快 - 你忘了ToList()实际上最终复制字典.尽管代码更快!;-)
这更快..因为ToList()实际上并没有最终复制字典,它只是复制它们内部元素的引用.基本上列表对象本身在内存中会有一个新地址,对象是一样的!!
博客文章消失但是yay Wayback机器:http://web.archive.org/web/20150311191313/http://diditwith.net/2006/10/05/PerformanceOfForeachVsListForEach.aspx

3> 小智..:

好吧,我迟到了,但这是我用的.如果有多个键("righter"键替换"lefter"键),它不会爆炸,可以合并多个词典(如果需要)并保留类型(限制它需要一个有意义的默认公共构造函数):

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(this T me, params IDictionary[] others)
        where T : IDictionary, new()
    {
        T newMap = new T();
        foreach (IDictionary src in
            (new List> { me }).Concat(others)) {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair p in src) {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

}


如果你把`me`改为`Dictionary `,你可以做`var newMap = new Dictionary (me,me.Comparer);`,你也避免额外的`T`类型参数.

4> orip..:

琐碎的解决方案是:

using System.Collections.Generic;
...
public static Dictionary
    Merge(IEnumerable> dictionaries)
{
    var result = new Dictionary();
    foreach (var dict in dictionaries)
        foreach (var x in dict)
            result[x.Key] = x.Value;
    return result;
}



5> JaredPar..:

请尝试以下方法

static Dictionary
    Merge(this IEnumerable> enumerable)
{
    return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value);
}



6> ctrlalt31337..:
Dictionary allTables = new Dictionary();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);



7> Ethan Reesor..:

以下适用于我.如果有重复项,它将使用dictA的值.

public static IDictionary Merge(this IDictionary dictA, IDictionary dictB)
    where TValue : class
{
    return dictA.Keys.Union(dictB.Keys).ToDictionary(k => k, k => dictA.ContainsKey(k) ? dictA[k] : dictB[k]);
}



8> codingatty..:

我很晚才参加派对,也许会遗漏一些东西,但如果要么没有重复的密钥,或者正如OP所说的那样,"如果发生碰撞,只要它是d的,那么将哪个值保存到dict并不重要.一致,"这个有什么问题(把D2合并到D1)?

foreach (KeyValuePair item in D2)
            {
                 D1[item.Key] = item.Value;
            }

这看起来很简单,也许太简单了,我想知道我是否遗漏了什么.这是我在一些代码中使用的,我知道没有重复的密钥.不过,我还在测试中,所以如果我忽略了某些东西,我现在很想知道,而不是后来发现.


最简洁,最易读的解决方案之一,也避免了许多其他人使用的ToList().

9> Andrew Harry..:

这是我使用的辅助函数:

using System.Collections.Generic;
namespace HelperMethods
{
    public static class MergeDictionaries
    {
        public static void Merge(this IDictionary first, IDictionary second)
        {
            if (second == null || first == null) return;
            foreach (var item in second) 
                if (!first.ContainsKey(item.Key)) 
                    first.Add(item.Key, item.Value);
        }
    }
}


`if(first == null)`那么这个逻辑是没用的,因为`first`不是`ref`而且没有返回.并且它不能是`ref`,因为它被声明为接口实例; 您需要删除错误检查或将其声明为类实例或只返回一个新的字典(没有扩展方法).

10> Bryan Watts..:

如何添加params过载?

此外,您应该键入它们以IDictionary获得最大的灵活性.

public static IDictionary Merge(IEnumerable> dictionaries)
{
    // ...
}

public static IDictionary Merge(params IDictionary[] dictionaries)
{
    return Merge((IEnumerable) dictionaries);
}


Downvoted,因为这根本不回答这个问题.
他通过注意你可以使DictionaryExtensions类变得更好来增加其他答案.也许这个问题应该成为一个wiki,或者是一个签到git的类......

11> GoldPaintedL..:

考虑到字典键查找和删除的性能,因为它们是哈希操作,并且考虑到问题的措辞是最好的方式,我认为下面是一个完全有效的方法,其他有点过于复杂,恕我直言.

    public static void MergeOverwrite(this IDictionary dictionary, IDictionary newElements)
    {
        if (newElements == null) return;

        foreach (var e in newElements)
        {
            dictionary.Remove(e.Key); //or if you don't want to overwrite do (if !.Contains()
            dictionary.Add(e);
        }
    }

或者,如果您在多线程应用程序中工作,并且您的字典无论如何都需要线程安全,那么您应该这样做:

    public static void MergeOverwrite(this ConcurrentDictionary dictionary, IDictionary newElements)
    {
        if (newElements == null || newElements.Count == 0) return;

        foreach (var ne in newElements)
        {
            dictionary.AddOrUpdate(ne.Key, ne.Value, (key, value) => value);
        }
    }

然后,您可以将其换行以使其处理字典的枚举.无论如何,你正在关注~O(3n)(所有条件都很完美),因为它.Add()Contains()在幕后做一些额外的,不必要的但实际上是免费的.我认为它不会好得多.

如果要限制对大型集合的额外操作,则应总结Count要合并的每个字典,并将目标字典的容量设置为该值,这样可以避免以后调整大小的成本.所以,最终产品是这样的......

    public static IDictionary MergeAllOverwrite(IList> allDictionaries)
    {
        var initSize = allDictionaries.Sum(d => d.Count);
        var resultDictionary = new Dictionary(initSize);
        allDictionaries.ForEach(resultDictionary.MergeOverwrite);
        return resultDictionary;
    }

请注意,我接受了IList这种方法...主要是因为如果你接受了一个IEnumerable,你已经打开了自己的同一组的多个枚举,如果从延迟的LINQ获得你的字典集合,这可能是非常昂贵的声明.

推荐阅读
ifx0448363
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有