While it is incredibly useful to be able to do set operations between the keys of a dictionary, I often wish that I could perform the set operations on the dictionaries themselves.
I found some recipes for taking the difference of two dictionaries but I found those to be quite verbose and felt there must be more pythonic answers.
Sets being mutable are not hashable, so they can't be used as dictionary keys.
tl;dr Recipe: {k:d1.get(k, k in d1 or d2[k]) for k in set(d1) | set(d2)}
and |
can be replaced with any other set operator.
Based @torek's comment, another recipe that might be easier to remember (while being fully general) is: {k:d1.get(k,d2.get(k)) for k in set(d1) | set(d2)}
.
Full answer below:
My first answer didn't deal correctly with values that evaluated to False. Here's an improved version which deals with Falsey values:
>>> d1 = {'one':1, 'both':3, 'falsey_one':False, 'falsey_both':None}
>>> d2 = {'two':2, 'both':30, 'falsey_two':None, 'falsey_both':False}
>>>
>>> print "d1 - d2:", {k:d1[k] for k in d1 if k not in d2} # 0
d1 - d2: {'falsey_one': False, 'one': 1}
>>> print "d2 - d1:", {k:d2[k] for k in d2 if k not in d1} # 1
d2 - d1: {'falsey_two': None, 'two': 2}
>>> print "intersection:", {k:d1[k] for k in d1 if k in d2} # 2
intersection: {'both': 3, 'falsey_both': None}
>>> print "union:", {k:d1.get(k, k in d1 or d2[k]) for k in set(d1) | set(d2)} # 3
union: {'falsey_one': False, 'falsey_both': None, 'both': 3, 'two': 2, 'one': 1, 'falsey_two': None}
The version for union
is the most general and can be turned into a function:
>>> def dict_ops(d1, d2, setop):
... """Apply set operation `setop` to dictionaries d1 and d2
...
... Note: In cases where values are present in both d1 and d2, the value from
... d1 will be used.
... """
... return {k:d1.get(k,k in d1 or d2[k]) for k in setop(set(d1), set(d2))}
...
>>> print "d1 - d2:", dict_ops(d1, d2, lambda x,y: x-y)
d1 - d2: {'falsey_one': False, 'one': 1}
>>> print "d2 - d1:", dict_ops(d1, d2, lambda x,y: y-x)
d2 - d1: {'falsey_two': None, 'two': 2}
>>> import operator as op
>>> print "intersection:", dict_ops(d1, d2, op.and_)
intersection: {'both': 3, 'falsey_both': None}
>>> print "union:", dict_ops(d1, d2, op.or_)
union: {'falsey_one': False, 'falsey_both': None, 'both': 3, 'two': 2, 'one': 1, 'falsey_two': None}
Where items are in both dictionaries, the value from d1
will be used. Of course we can return the value from d2
instead by changing the order of the function arguments.
>>> print "union:", dict_ops(d2, d1, op.or_)
union: {'both': 30, 'falsey_two': None, 'falsey_one': False, 'two': 2, 'one': 1, 'falsey_both': False}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With