25

I have two dictionaries in Python:

d1 = {'a': 10, 'b': 9, 'c': 8, 'd': 7}
d2 = {'a': 1, 'b': 2, 'c': 3, 'e': 2}

I want to substract values between dictionaries d1-d2 and get the result:

d3 = {'a': 9, 'b': 7, 'c': 5, 'd': 7 }

Now I'm using two loops but this solution is not too fast

for x,i in enumerate(d2.keys()):
        for y,j in enumerate(d1.keys()):
Colargol
  • 749
  • 4
  • 14
  • 26
  • possible duplicate of [Is there any pythonic way to combine two dicts (adding values for keys that appear in both)?](http://stackoverflow.com/questions/11011756/is-there-any-pythonic-way-to-combine-two-dicts-adding-values-for-keys-that-appe) – Martijn Pieters Jul 16 '13 at 08:49

4 Answers4

40

I think a very Pythonic way would be using dict comprehension:

d3 = {key: d1[key] - d2.get(key, 0) for key in d1}

Note that this only works in Python 2.7+ or 3.

Erfa
  • 639
  • 6
  • 9
25

Use collections.Counter, iif all resulting values are known to be strictly positive. The syntax is very easy:

>>> from collections import Counter
>>> d1 = Counter({'a': 10, 'b': 9, 'c': 8, 'd': 7})
>>> d2 = Counter({'a': 1, 'b': 2, 'c': 3, 'e': 2})
>>> d3 = d1 - d2
>>> print d3
Counter({'a': 9, 'b': 7, 'd': 7, 'c': 5})

Mind, if not all values are known to remain strictly positive:

  • elements with values that become zero will be omitted in the result
  • elements with values that become negative will be missing, or replaced with wrong values. E.g., print(d2-d1) can yield Counter({'e': 2}).
FlorianH
  • 548
  • 7
  • 17
TerryA
  • 56,204
  • 11
  • 116
  • 135
14

Just an update to Haidro answer.

Recommended to use subtract method instead of "-".

d1.subtract(d2)

When - is used, only positive counters are updated into dictionary. See examples below

c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
a = c-d
print(a)        # --> Counter({'a': 3})
c.subtract(d)
print(c)        # --> Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

Please note the dictionary is updated when subtract method is used.

And finally use dict(c) to get Dictionary from Counter object

Hemanth
  • 315
  • 1
  • 7
  • Do you mind if I add this into my answer? I'll reference yours :) – TerryA Jul 16 '13 at 09:00
  • Actually, while testing this, it doesn't returned the OP's wanted dictionary. This returns `e = -2`, where the OP just wants to remove the `e`. – TerryA Jul 16 '13 at 09:04
  • And, although not a major problem, it doesn't return the subtracted dictionary (but really, I don't think this will matter) – TerryA Jul 16 '13 at 09:04
  • Also note that `subtract` can handle `dict` types, so there is no need to convert `d`. – tamasgal Jul 16 '13 at 11:20
  • Yessss finally needed this bit of code for negatives! – RustyShackleford Jan 31 '20 at 17:49
  • what if I dont want to change Counter c. i want to assign the result to a variable. i try it give me a None type. anyway to use e = c.subtract(d) ? – jon rios Apr 19 '21 at 13:29
4

Haidro posted an easy solution, but even without collections you only need one loop:

d1 = {'a': 10, 'b': 9, 'c': 8, 'd': 7}
d2 = {'a': 1, 'b': 2, 'c': 3, 'e': 2}
d3 = {}

for k, v in d1.items():
    d3[k] = v - d2.get(k, 0) # returns value if k exists in d2, otherwise 0

print(d3) # {'c': 5, 'b': 7, 'a': 9, 'd': 7}
joente
  • 743
  • 5
  • 8