在C#中,如何对两个字典的值进行求和?

12

我有两个具有相同结构的字典:

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想出一个解决方案,但卡在了瓶颈上。


这两个字典是否保证具有相同的键集? - Carlos
在这种情况下,是的,@Carlos。但是看到一种解决方案,使字典共享某些键而不共享其他键会很有趣。 - George Stocker
6个回答

15

以下解决方案并不是最高效的(因为它只是将两个字典视为枚举对象),但它能够正常工作并且相当清晰:

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);

编辑了您的答案,展示如何将结果查询返回到字典中。 - George Stocker

5
如果你有一个铸铁的保证,两组钥匙是相同的:
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
(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) 

这应该就可以了。

编辑:可能更有效率(不确定join是如何实现的)

(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)

谢谢你的回答;这个答案也可以帮助你计算对象之间的差异。 - George Stocker

4

嗯,我不知道哪个更高效,但是你的解决方案为什么不易读呢?

有什么问题吗?

  foreach (string key in d1.Keys)
  {
     d3.Add(key,d1[key]+d2[key]);
  }

我认为这个解决方案比一些linq解决方案更清晰。尽管我没有测试过它,但我认为它的性能可能更好,因为它只在一个字典中枚举键而不是值,你可以使用实际的哈希(或字典的底层实现)来查找值,这是获取它们的最快速度。

编辑:

对于键不总是相同的解决方案,如果你只想获取共享的键,你只需要添加一行代码;

foreach (string key in d1.Keys)
  {
     if(d2.ContainsKey(key)
        d3.Add(key,d1[key]+d2[key]);
  }

编辑2:

如果要获取所有不同的键/值,可以这样做:

   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]);
       }

当然,d2 有一些不在 d1 中的键仍然是个问题。 - Dan Tao
@Dan Tao 是的,那只对共享密钥起作用。 编辑:好的,我添加了解决方案 :P - Francisco Noriega

2

我写了一个小扩展方法,可以合并具有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 ) ) );
    }

2

这是怎么样的情况?

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);

至少这有点有意思吧?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接