我在C#6.0中有两个字典,我想以聪明的方式合并它们。
将第一个字典foo
作为:
var foo = new Dictionary<string, string>
{
{"a", "10"},
{"b", "20"},
{"c", "30"},
};
第二个字典bar
为:
var bar = new Dictionary<string, string>
{
{"a", "333"},
{"e", "444"},
{"f", "555"},
};
我想用一个逻辑将它们合并到一个字典中:
foo
中但在bar
中没有,请在新词典中忽略它bar
中但在foo
中不是,请将其带入新词典foo
和bar
中,请在新词典中取foo
的值这是我的预期结果:
var result = new Dictionary<string, string>
{
{"a", "10"}, //this comes from foo
{"e", "444"}, //this comes from bar
{"f", "555"}, //this comes from bar
};
如果没有forloop(LINQ表达式没问题),有没有一种聪明的方法来处理这个问题?
答案 0 :(得分:1)
您可以使用HashSet<T>
方法和LINQ:
1)
var fooKeys = new HashSet<string>(foo.Keys);
var barKeys = new HashSet<string>(bar.Keys);
fooKeys.IntersectWith(barKeys); // remove all from fooKeys which are not in both
barKeys.ExceptWith(fooKeys); // remove all from barKeys which are remaining in fooKeys and also in barKeys
Dictionary<string, string> result = fooKeys
.Select(fooKey => new KeyValuePair<string, string>(fooKey, foo[fooKey]))
.Concat(barKeys.Select(bKey => new KeyValuePair<string, string>(bKey, bar[bKey])))
.ToDictionary(kv => kv.Key, kv => kv.Value);
这是安全的,因为两者都相互排斥。它也非常有效,因为这些HashSet
方法具有两组O(n)
复杂度。
如果您认为不可理解,也许您更喜欢这个:
2)
var inBoth = from kv1 in foo
join kv2 in bar
on kv1.Key equals kv2.Key
select kv1;
var onlyInBar = bar.Keys.Except(foo.Keys)
.Select(b => new KeyValuePair<string, string>(b, bar[b]));
Dictionary<string, string> result = new Dictionary<string, string>();
foreach (var kv in inBoth.Concat(onlyInBar))
result.Add(kv.Key, kv.Value);
第一个查询使用一个连接(在查询语法中更易读),它只返回第一个字典中的键值对,其中键也存在于第二个字典中。第二个查询使用Enumerable.Except
从第一个字典中排除第二个字典中的所有字典。 Enumerable.Join
和Enumerable.Except
都在引擎盖下使用,因此非常有效。
值得注意的是,由于LINQ的延迟执行,两个查询仅在foreach (var kv in inBoth.Concat(onlyInBar))
而不是之前执行。
可能是最简单,最易读的方法,&#34; LINQ left-outer-join&#34;:
3)
KeyValuePair<string, string> defaultPair = default(KeyValuePair<string, string>);
var query = from barKv in bar
join fooKv in foo
on barKv.Key equals fooKv.Key into gj_bf
from bf in gj_bf.DefaultIfEmpty()
select bf.Equals(defaultPair) ? barKv : bf;
foreach (var kv in query)
result.Add(kv.Key, kv.Value);
答案 1 :(得分:1)
您可以像这样使用GroupJoin
:
var res =
bar
.GroupJoin(
foo,
kvp => kvp.Key,
kvp => kvp.Key,
(kvp, g) => new KeyValuePair<string, string>(kvp.Key, g.FirstOrDefault().Value ?? kvp.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
这里的诀窍就是GroupJoin
吧!这样,bar中的所有内容都会出现在最终结果中,对于相同的键,连接结果将是来自第二个集合的匹配结果的IEnumerable
,在您的情况下是foo,因为它是Dictionary
所以匹配的结果将只包含一个元素,您需要做的就是获取其值。如果不匹配(条形但不在foo中),匹配的结果集合将为空,因此FirstOrDefault()
将返回默认值KeyValuePair<string, string>
,其中Key和Value都设置为null。所以在这种情况下,我们只需从第一个集合中获取Value(在您的案例栏中)。
答案 2 :(得分:1)
(简单)Linq解决方案:
var newDict = new Dictionary<string, string>();
var toIncludeFromFoo = bar.Keys.Intersect(foo.Keys).ToList();
toIncludeFromFoo.ForEach(x => newDict [x] = foo[x]);
var toAddFromBar = bar.Keys.Except(foo.Keys).ToList();
toAddFromBar.ForEach(x => newDict [x] = bar[x]);
答案 3 :(得分:1)
您的逻辑可以简化为:
结果将包含来自bar
的所有密钥,如果存在,则取自foo
的值,否则取自bar
。
翻译成这样的东西:
var result = bar.ToDictionary(barItem => barItem.Key, barItem =>
foo.ContainsKey(barItem.Key) ? foo[barItem.Key] : barItem.Value);
或更长一点,但更优化:
var result = bar.ToDictionary(barItem => barItem.Key, barItem =>
{ string fooValue; return foo.TryGetValue(barItem.Key, out fooValue) ? fooValue : barItem.Value; });