Как суммировать значения из двух словарей в С#?
У меня есть два словаря с одинаковой структурой:
Dictionary<string, int> foo = new Dictionary<string, int>()
{
{"Table", 5 },
{"Chair", 3 },
{"Couch", 1 }
};
Dictionary<string, int> bar = new Dictionary<string, int>()
{
{"Table", 4 },
{"Chair", 7 },
{"Couch", 8 }
};
Я хотел бы суммировать значения словарей вместе и возвращать третьи словари с ключами и общие значения для каждого ключа:
Table, 9
Chair, 10
Couch, 9
Мое текущее решение состоит в том, чтобы зацикливать словарь и вытащить его таким образом, но я знаю, что это решение не самое результативное или наиболее читаемое. Тем не менее, я нажимаю на кирпичную стену, пытаясь найти решение в LINQ.
Ответы
Ответ 1
Ниже приведено не самое эффективное решение (потому что он просто рассматривает оба словаря как перечисляемые), но он будет работать, и это совершенно ясно:
Dictionary<string, int> result = (from e in foo.Concat(bar)
group e by e.Key into g
select new { Name = g.Key, Count = g.Sum(kvp => kvp.Value) })
.ToDictionary(item => item.Name, item => item.Count);
Ответ 2
(from a in foo
join b in bar on a.Key equals b.Key
select new { Key = a.Key, Value = a.Value + b.Value })
.ToDictionary(a => a.Key,a => a.Value)
Это должно сделать это.
EDIT: может быть более эффективным (не знаете, как это реализовано)
(from a in foo
let b = bar.ContainsKey(a.Key) ? (int?)bar[a.Key] : null
select new { Key = a.Key, Value = a.Value + (b != null ? b : 0) }
).ToDictionary(a => a.Key, a => a.Value)
Ответ 3
Если у вас есть чугунная гарантия, что два набора ключей одинаковы:
Dictionary<string, int> Res2 = foo.ToDictionary(orig => orig.Key, orig => orig.Value + bar[orig.Key]);
Лучше всего я мог бы придумать, если ключи не одинаковы:
var AllKeys = foo.Keys.Union(bar.Keys);
var res3 = AllKeys.ToDictionary(key => key, key => (foo.Keys.Contains(key)?foo[key] : 0) + (bar.Keys.Contains(key)?bar[key] : 0));
Ответ 4
Ммм, я не знаю, что больше для форманта, но как ваше решение не читается?
Что случилось с
foreach (string key in d1.Keys)
{
d3.Add(key,d1[key]+d2[key]);
}
?
Я действительно считаю его более понятным, чем некоторые из решений linq. Несмотря на то, что я не тестировал его, я думаю, что он может иметь лучшую производительность, поскольку он перечисляет только ключи в одном словаре, а не значениях, вы должны использовать фактическое хеширование (или что-то другое, лежащее в основе реализации словаря) найдите значения, которые являются самым быстрым способом их получения.
EDIT:
для решения, где ключи не всегда будут одинаковыми, если вы хотите получать только общие, вам нужно только добавить строку;
foreach (string key in d1.Keys)
{
if(d2.ContainsKey(key)
d3.Add(key,d1[key]+d2[key]);
}
EDIT2:
Чтобы получить все ключи/значения, если они не совпадают, это будет примерно так:
foreach (string key in d1.Keys)
{
if(d2.ContainsKey(key)
d3.Add(key,d1[key]+d2[key]);
else
d3.Add(key,d1[key])
}
foreach (string key in d2.keys)
{
if(!d1.ContainsKey(key) // only get keys that are unique to d2
d3.Add(key,d2[key]);
}
Ответ 5
Как насчет этого?
var fooBar = foo.Keys
.Union(bar.Keys)
.Select(
key => {
int fval = 0, bval = 0;
foo.TryGetValue(key, out fval);
bar.TryGetValue(key, out bval);
return new KeyValuePair<string, int>(key, fval + bval);
}
)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
По крайней мере, это (вроде?) аккуратно.
Ответ 6
Я написал небольшой метод расширения, который объединит список словарей с значениями Int. Я использовал код из этого вопроса, чтобы сделать это, поэтому я разделяю
public static Dictionary<TSource, Int32> MergeIntDictionary<TSource>( this ICollection<Dictionary<TSource, Int32>> source )
{
return source.Aggregate( ( cur, next ) => cur.Concat( next )
.GroupBy( o => o.Key )
.ToDictionary( item => item.Key, item => item.Sum( o => o.Value ) ) );
}