views:

307

answers:

5

I have two dictionaries with the same structure:

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

I'd like to sum the values of the dictionaries together and return a third dictionaries with the keys, and the total values for each key:

Table, 9
Chair, 10
Couch, 9

My current solution is to loop through the dictionary and pull them out that way, but I know that solution isn't the most performant or most readable. However, I'm hitting a brick wall trying to come up with a solution in LINQ.

+7  A: 

The following isn't the most efficient solution (because it simply treats both dictionaries as enumerables), but it will work and it is quite clear:

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);
Tomas Petricek
@Ben: Thanks for the correction, I just realized that too.
Tomas Petricek
Edited your answer to show how to get the resulting query back into a dictionary.
George Stocker
@George: Thanks
Tomas Petricek
More Clear example.
Mohanavel
+3  A: 
(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) 

That should do it.

EDIT: Might be more efficient (not sure how the join is implemented)

(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)
Stephan
Thanks for the answer; this answer also helps if you want to compute deltas between the objects.
George Stocker
+1  A: 

If you have a cast iron guarantee that the two sets of keys are the same:

Dictionary<string, int> Res2 = foo.ToDictionary(orig => orig.Key, orig => orig.Value + bar[orig.Key]);

Best I could come up with if keys aren't same set:

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));
Carlos
+2  A: 

Mmm, I don't know which is more per formant, but how is your solution not readable?

Whats wrong with

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

?

I actually think its more clear than some of the linq solutions. Even though I haven't tested it, I think it could have better performance, since it only enumerates the keys in one dictionary and not the values, you'd use the actual hashing (or whatever is the underlying implementation of the dictionary) to find the values, which is the fastest way to get them.

EDIT:

for the solution where keys wouldnt always be the same, if you only want to get shared ones,you only need to add a line;

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

EDIT2:

In order to get all keys/values if they are not the same, then it'd be like this:

   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]);
       }
Francisco Noriega
Well, there's still the matter of `d2` having keys that aren't in `d1`, of course...
Dan Tao
@Dan Tao yeah, that would only work for the shared keys.EDIT: okay I added the solution for that :P
Francisco Noriega
+1  A: 

What about something like this?

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

At least it's (kind of?) neat.

Dan Tao