ori*_*rip 464 c# merge dictionary
Dictionary<T1,T2>在C#中合并2个或更多字典()的最佳方法是什么?(像LINQ这样的3.0功能很好).
我正在考虑一种方法签名:
public static Dictionary<TKey,TValue>
Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);
Run Code Online (Sandbox Code Playgroud)
要么
public static Dictionary<TKey,TValue>
Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);
Run Code Online (Sandbox Code Playgroud)
编辑:从JaredPar和Jon Skeet得到一个很酷的解决方案,但我正在考虑处理重复键的东西.在发生碰撞的情况下,只要它是一致的,将哪个值保存到dict并不重要.
Jon*_*eet 295
这部分取决于你遇到重复的事情.例如,你可以这样做:
var result = dictionaries.SelectMany(dict => dict)
.ToDictionary(pair => pair.Key, pair => pair.Value);
Run Code Online (Sandbox Code Playgroud)
如果你得到任何重复的密钥,那将会爆炸.
编辑:如果您使用ToLookup,那么您将获得一个查找,每个键可以有多个值.然后,您可以将其转换为字典:
var result = dictionaries.SelectMany(dict => dict)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
Run Code Online (Sandbox Code Playgroud)
这有点难看 - 而且效率低下 - 但这是在代码方面做到最快的方法.(诚然,我没有测试过.)
您当然可以编写自己的ToDictionary2扩展方法(名称更好,但我现在没有时间考虑一个) - 这不是很难做,只是覆盖(或忽略)重复键.重要的一点(在我看来)是使用SelectMany,并意识到字典支持迭代其键/值对.
Jon*_*ved 250
我会这样做:
dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value));
Run Code Online (Sandbox Code Playgroud)
简单易行.根据这篇博客文章,它比大多数循环更快,因为它的底层实现通过索引而不是枚举器访问元素(参见本答案).
如果存在重复,它当然会抛出异常,因此您必须在合并之前进行检查.
小智 97
好吧,我迟到了,但这是我用的.如果有多个键("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<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;
}
}
Run Code Online (Sandbox Code Playgroud)
ori*_*rip 45
琐碎的解决方案是:
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;
}
Run Code Online (Sandbox Code Playgroud)
Jar*_*Par 20
请尝试以下方法
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);
}
Run Code Online (Sandbox Code Playgroud)
ctr*_*373 17
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);
Run Code Online (Sandbox Code Playgroud)
Eth*_*sor 14
以下适用于我.如果有重复项,它将使用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]);
}
Run Code Online (Sandbox Code Playgroud)
cod*_*tty 11
我很晚才参加派对,也许会遗漏一些东西,但如果要么没有重复的密钥,或者正如OP所说的那样,"如果发生碰撞,只要它是d的,那么将哪个值保存到dict并不重要.一致,"这个有什么问题(把D2合并到D1)?
foreach (KeyValuePair<string,int> item in D2)
{
D1[item.Key] = item.Value;
}
Run Code Online (Sandbox Code Playgroud)
这看起来很简单,也许太简单了,我想知道我是否遗漏了什么.这是我在一些代码中使用的,我知道没有重复的密钥.不过,我还在测试中,所以如果我忽略了某些东西,我现在很想知道,而不是后来发现.
小智 10
选项 1 :如果您确定两个字典中没有重复的键,这取决于您想要发生的情况。比你能做的:
var result = dictionary1.Union(dictionary2).ToDictionary(k => k.Key, v => v.Value)
Run Code Online (Sandbox Code Playgroud)
注意:如果您在字典中得到任何重复的键,这将引发错误。
选项 2 :如果您可以有重复的键,那么您必须使用 where 子句来处理重复的键。
var result = dictionary1.Union(dictionary2.Where(k => !dictionary1.ContainsKey(k.Key))).ToDictionary(k => k.Key, v => v.Value)
Run Code Online (Sandbox Code Playgroud)
注意:它不会得到重复的密钥。如果有任何重复的键,它就会得到字典 1 的键。
选项 3:如果您想使用 ToLookup。然后你会得到一个查找,每个键可以有多个值。您可以将该查找转换为字典:
var result = dictionaries.SelectMany(dict => dict)
.ToLookup(pair => pair.Key, pair => pair.Value)
.ToDictionary(group => group.Key, group => group.First());
Run Code Online (Sandbox Code Playgroud)
这是我使用的辅助函数:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如何添加params过载?
此外,您应该键入它们以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);
}
Run Code Online (Sandbox Code Playgroud)
基于上面的答案,但添加一个 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));
}
Run Code Online (Sandbox Code Playgroud)
考虑到字典键查找和删除的性能,因为它们是哈希操作,并且考虑到问题的措辞是最好的方式,我认为下面是一个完全有效的方法,其他有点过于复杂,恕我直言.
public static void MergeOverwrite<T1, T2>(this IDictionary<T1, T2> dictionary, IDictionary<T1, T2> 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);
}
}
Run Code Online (Sandbox Code Playgroud)
或者,如果您在多线程应用程序中工作,并且您的字典无论如何都需要线程安全,那么您应该这样做:
public static void MergeOverwrite<T1, T2>(this ConcurrentDictionary<T1, T2> dictionary, IDictionary<T1, T2> newElements)
{
if (newElements == null || newElements.Count == 0) return;
foreach (var ne in newElements)
{
dictionary.AddOrUpdate(ne.Key, ne.Value, (key, value) => value);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以将其换行以使其处理字典的枚举.无论如何,你正在关注~O(3n)(所有条件都很完美),因为它.Add()会Contains()在幕后做一些额外的,不必要的但实际上是免费的.我认为它不会好得多.
如果要限制对大型集合的额外操作,则应总结Count要合并的每个字典,并将目标字典的容量设置为该值,这样可以避免以后调整大小的成本.所以,最终产品是这样的......
public static IDictionary<T1, T2> MergeAllOverwrite<T1, T2>(IList<IDictionary<T1, T2>> allDictionaries)
{
var initSize = allDictionaries.Sum(d => d.Count);
var resultDictionary = new Dictionary<T1, T2>(initSize);
allDictionaries.ForEach(resultDictionary.MergeOverwrite);
return resultDictionary;
}
Run Code Online (Sandbox Code Playgroud)
请注意,我接受了IList<T>这种方法...主要是因为如果你接受了一个IEnumerable<T>,你已经打开了自己的同一组的多个枚举,如果从延迟的LINQ获得你的字典集合,这可能是非常昂贵的声明.
| 归档时间: |
|
| 查看次数: |
255390 次 |
| 最近记录: |