30

Possible Duplicate:
python dict.add_by_value(dict_2) ?

My input is two dictionaries that have string keys and integer values. I want to add the two dictionaries so that the result has all the keys of the input dictionaries, and the values are the sum of the input dictionaries' values.

For clarity, if a key appears in only one of the inputs, that key/value will appear in the result, whereas if the key appears in both dictionaries, the sum of values will appear in the result.

For example, say my input is:

a = dict()
a['cat'] = 1
a['fish'] = 10
a['aardvark'] = 1000

b = dict()
b['cat'] = 2
b['dog'] = 200
b['aardvark'] = 2000

I would like the result to be:

{'cat': 3, 'fish': 10, 'dog': 200, 'aardvark': 3000}

Knowing Python there must be a one-liner to get this done (it doesn't really have to be one line...). Any thoughts?

Community
  • 1
  • 1
Roee Adler
  • 33,434
  • 32
  • 105
  • 133

4 Answers4

51

How about that:

dict( [ (n, a.get(n, 0)+b.get(n, 0)) for n in set(a)|set(b) ] )

Or without creating an intermediate list (generator is enough):

dict( (n, a.get(n, 0)+b.get(n, 0)) for n in set(a)|set(b) )

Post Scriptum:

As a commentator addressed correctly, there is a way to implement that easier with the new (from Py2.7) collections.Counter class. As much I remember, this version was not available when I wrote the answer:

from collections import Counter
dict(Counter(a)+Counter(b))
Juergen
  • 12,378
  • 7
  • 39
  • 55
  • 1
    +1 nice, although the square brackets aren't necessary as dict() will take a generator – cobbal Jun 23 '09 at 08:13
  • Thats right! Just a habit of me. I saw it afterwards. – Juergen Jun 23 '09 at 08:16
  • 2
    +1: nice one-liner. However, I'd do set(a).union(b), so as not to create an intermediate set [set(b)]. – Eric O. Lebigot Jun 23 '09 at 13:26
  • @EOL: Thanks, thats right. There is always a way to squeeze out a little more ... – Juergen Jun 23 '09 at 14:00
  • 1
    To contribute: `res = dict( (n, a.get(n, 0)+b.get(n, 0)) for n in set(a)|set(b) ) \\ {k: filter(lambda x: x, v) for k,v in res.items() if not all(v)}` will filter out any empty values from iterable values in the dictionary. Dict comprehension requires Python 2.7+ – mattdeboard Dec 15 '11 at 17:32
  • 2
    As the #1 answer, could you change this to suggest using collections.Counter, as in "from collections import Counter" then "dict(Counter(a)+Counter(b))" ? – Andrew Dalke Mar 07 '14 at 12:38
15

result in a:

for elem in b:
    a[elem] = a.get(elem, 0) + b[elem]

result in c:

c = dict(a)
for elem in b:
    c[elem] = a.get(elem, 0) + b[elem]
Adrian Panasiuk
  • 7,249
  • 5
  • 33
  • 54
15

Not in one line, but ...

import itertools
import collections
a = dict()
a['cat'] = 1
a['fish'] = 10
a['aardvark'] = 1000
b = dict()
b['cat'] = 2
b['dog'] = 200
b['aardvark'] = 2000
c = collections.defaultdict(int)
for k, v in itertools.chain(a.iteritems(), b.iteritems()):
    c[k] += v

You can easily extend it to a larger number of dictionaries.

  • itertools.chain is a nice solution, when maximum speed is needed, since in my solution a temporary set is created. I suppose, chain is faster for big dicts. – Juergen Jun 23 '09 at 08:20
  • Well, I didn't use it for speed here. I just don't need to check if there are elements from a that are missing in b and vice versa. –  Jun 23 '09 at 08:23
4

One liner (as sortof requested): get key lists, add them, discard duplicates, iterate over result with list comprehension, return (key,value) pairs for the sum if the key is in both dicts, or just the individual values if not. Wrap in dict.

>>> dict([(x,a[x]+b[x]) if (x in a and x in b) else (x,a[x]) if (x in a) else (x,b[x]) for x in set(a.keys()+b.keys())])
{'aardvark': 3000, 'fish': 10, 'dog': 200, 'cat': 3}
Markus
  • 3,447
  • 3
  • 24
  • 26