Dictionary
在C#中合并2个或更多字典()的最佳方法是什么?(像LINQ这样的3.0功能很好).
我正在考虑一种方法签名:
public static DictionaryMerge (Dictionary [] dictionaries);
要么
public static DictionaryMerge (IEnumerable > dictionaries);
编辑:从JaredPar和Jon Skeet得到一个很酷的解决方案,但我正在考虑处理重复键的东西.在发生碰撞的情况下,只要它是一致的,将哪个值保存到dict并不重要.
这部分取决于你遇到重复的事情.例如,你可以这样做:
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,并意识到字典支持迭代其键/值对.
我会这样做:
dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value));
简单易行.根据这篇博客文章,它比大多数循环更快,因为它的底层实现通过索引而不是枚举器访问元素(参见本答案).
如果存在重复,它当然会抛出异常,因此您必须在合并之前进行检查.
好吧,我迟到了,但这是我用的.如果有多个键("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; } }
琐碎的解决方案是:
using System.Collections.Generic; ... public static DictionaryMerge (IEnumerable > dictionaries) { var result = new Dictionary (); foreach (var dict in dictionaries) foreach (var x in dict) result[x.Key] = x.Value; return result; }
请尝试以下方法
static DictionaryMerge (this IEnumerable > enumerable) { return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value); }
DictionaryallTables = new Dictionary (); allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
以下适用于我.如果有重复项,它将使用dictA的值.
public static IDictionaryMerge (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]); }
我很晚才参加派对,也许会遗漏一些东西,但如果要么没有重复的密钥,或者正如OP所说的那样,"如果发生碰撞,只要它是d的,那么将哪个值保存到dict并不重要.一致,"这个有什么问题(把D2合并到D1)?
foreach (KeyValuePairitem in D2) { D1[item.Key] = item.Value; }
这看起来很简单,也许太简单了,我想知道我是否遗漏了什么.这是我在一些代码中使用的,我知道没有重复的密钥.不过,我还在测试中,所以如果我忽略了某些东西,我现在很想知道,而不是后来发现.
这是我使用的辅助函数:
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); } } }
如何添加params
过载?
此外,您应该键入它们以IDictionary
获得最大的灵活性.
public static IDictionaryMerge (IEnumerable > dictionaries) { // ... } public static IDictionary Merge (params IDictionary [] dictionaries) { return Merge((IEnumerable ) dictionaries); }
考虑到字典键查找和删除的性能,因为它们是哈希操作,并且考虑到问题的措辞是最好的方式,我认为下面是一个完全有效的方法,其他有点过于复杂,恕我直言.
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 IDictionaryMergeAllOverwrite (IList > allDictionaries) { var initSize = allDictionaries.Sum(d => d.Count); var resultDictionary = new Dictionary (initSize); allDictionaries.ForEach(resultDictionary.MergeOverwrite); return resultDictionary; }
请注意,我接受了IList
这种方法...主要是因为如果你接受了一个IEnumerable
,你已经打开了自己的同一组的多个枚举,如果从延迟的LINQ获得你的字典集合,这可能是非常昂贵的声明.