6

I needed to compare 2 dictionaries to find the set of keys in one dictionary which was not in the other.

I know that Python set objects support:

set3=set1-set2

but I can't do:

dict3=dict1-dict2

or:

missingKeys=dict1.keys()-dict2.keys()

(I was a little surprised by the last point, because in Java the keys are a Set object.) One solution is:

missingKeys=set(dict1.keys())-set(dict2.keys())

is there a better or more concise way to do this?

Sam Goldberg
  • 6,711
  • 8
  • 52
  • 85
  • I think the last line is sufficiently concise myself, but... I guess a more interesting question is "How to remove all z in Y from X?" where X and Y are lists. This would be useful where duplicate z's exist in X that do appear in Y should be left alone, for instance. –  Apr 22 '12 at 23:04
  • @pst:Well, it feels a little weird to create set objects, just to leverage the difference() function... – Sam Goldberg Apr 22 '12 at 23:05
  • On the other hand, because it *is* a set, it can leverage a better O ... for list differences using comprehension is easy, but a tad more wordy. Still same performance if the "probed" list is converted to a `set`, though. –  Apr 22 '12 at 23:06

4 Answers4

15

Python 2.7:

>>> d = {1:2, 2:3, 3:4}
>>> d2 = {2:20, 3:30}
>>> set(d)-set(d2)
set([1])

Python 3.2:

>>> d = {1:2, 2:3, 3:4}
>>> d2 = {2:20, 3:30}
>>> d.keys()-d2.keys()
{1}
DSM
  • 342,061
  • 65
  • 592
  • 494
  • So you are saying that Python 3.2 dict.keys() method returns a set (like Java)? Any reason why earlier version didn't do that? – Sam Goldberg Apr 23 '12 at 00:08
  • 2
    @SamGoldberg: no, it actually returns a dict_keys object, not a set, but it has many set-like operations. See, e.g. [this question](http://stackoverflow.com/questions/7296716/what-is-dict-keys-dict-items-and-dict-values). – DSM Apr 23 '12 at 00:19
  • 1
    `d.keys() - d2` or even `d - d2.keys()` also work :) Just a general note, they're less readable, IMHO. – Niklas B. Apr 23 '12 at 19:21
  • How about the time complexity? Will `d.keys()-d2.keys()` work in `O(|len(d2)|)` as expected? – Tregoreg Feb 12 '14 at 20:52
  • Python 2.7 has a backport of Python 3's `.keys()`, so you can also do `d.viewkeys() - d2.viewkeys()`. – rescdsk Nov 14 '14 at 23:39
4

For portable way of doing it I would suggest using dict.viewkeys in Python 2.7 - it is backport of Python 3.x dict.keys and is automatically converted by 2to3.

Example:

>>> left = {1: 2, 2: 3, 3: 4}
>>> right = {2: 20, 3:30}
>>> left.viewkeys() - right.viewkeys()
set([1])
3

Maybe

[x for x in dict1.keys() if x not in dict2.keys()]
user1245262
  • 6,968
  • 8
  • 50
  • 77
  • 2
    I think this could be even a little more concise: `[x for x in dict1.keys() if x not in dict2]` since the `x in dict.keys()` is the same as `x in dict` – mgilson Apr 22 '12 at 23:38
  • @mgilson: as long as you pointed out the `x in dict` is the same as `x in dict.keys()`, then the complete reduction is `[x for x in dict1 if x not in dict2]`. – Sam Goldberg Apr 23 '12 at 00:15
  • 1
    @SamGoldberg You're correct. The reason I didn't think of it is because `for x in dict` calls the `__iter__` method on a dict whereas `if x in dict` calls the `__contains__` method. So, in this case, the `in` does two different things -- they just happen to have the same result. Anyway: `[x for x in dict1 if x not in dict2]` is most concise as you pointed out. – mgilson Apr 23 '12 at 00:54
  • Both answers were good, but accepting this answer because I am using Python 2.7. Being new to Python, this answer also helped me be more conscious of the [for ... in ... if ...] syntax, which is less natural to me than the d1-d2 syntax. – Sam Goldberg May 04 '12 at 17:26
  • To compute difference between sets of values you may use sets like in the response of Zbyszek Mandziejewicz. – Julien Palard Jul 14 '14 at 20:16
1

This should work in Python 2.7 and 3.x:

>>> keys = getattr(dict, 'viewkeys', dict.keys)
>>> left = {1: 2, 2: 3, 3: 4}
>>> right = {2: 20, 3:30} 
>>> list(keys(left) - keys(right))
[1]
rescdsk
  • 8,739
  • 4
  • 36
  • 32