300

I want to do something like:

foo = {
    'foo': 1,
    'zip': 2,
    'zam': 3,
    'bar': 4
}

if ("foo", "bar") in foo:
    #do stuff

How do I check whether both foo and bar are in dict foo?

Rodrigue
  • 3,617
  • 2
  • 37
  • 49

24 Answers24

490

Well, you could do this:

>>> if all(k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!
Martin
  • 2,347
  • 1
  • 21
  • 21
hughdbrown
  • 47,733
  • 20
  • 85
  • 108
  • 12
    +1, I like this better than Greg's answer because it's more concise AND faster (no building of irrelevant temporary list, AND full exploitation of short-circuiting). – Alex Martelli Aug 17 '09 at 02:34
  • 10
    I love all() and any(). They make so many algorithms so much cleaner. – hughdbrown Aug 17 '09 at 03:49
  • I ultimately ended up using this solution. It seemed the best for larger datasets. When checking for say 25 or 30 keys. –  Aug 17 '09 at 04:37
  • 4
    It's a good solution thanks to short-circuiting, especially if the test fails more often than not; unless you can create the set of keys of interest just once and check it many times, in which case `set` is superior. As usual... measure it!-) – Alex Martelli Aug 17 '09 at 04:57
  • 1
    I use this whenever it looks nicer than the "normal" way, with all the and's or the or's... it's also nice 'cause you can use either "all" or "any"... in addition you can either have "k in foo" or "k not in foo" depending on the test you are trying to perform – Terence Honles Aug 17 '09 at 08:57
  • 1
    checkout @claytonk's solution, more concise and a 60% performance boost – miraculixx Jan 31 '15 at 12:31
  • With Python 3, we retain short-circuiting if we use `map()`, as in: `all(map(foo.has_key, ("foo","bar")))`. – John Zwinck Nov 24 '16 at 12:52
160
if {"foo", "bar"} <= myDict.keys(): ...

If you're still on Python 2, you can do

if {"foo", "bar"} <= myDict.viewkeys(): ...

If you're still on a really old Python <= 2.6, you can call set on the dict, but it'll iterate over the whole dict to build the set, and that's slow:

if set(("foo", "bar")) <= set(myDict): ...
user2357112
  • 260,549
  • 28
  • 431
  • 505
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • looks good! The only thing I don't like is that you have to create temporary sets, but it's very compact. So I must say... nice use of sets! – Terence Honles Aug 17 '09 at 09:02
  • 21
    In python 3 you can say `set(("foo","bar")) <= myDict.keys()` which avoids the temporary set, so is much faster. For my testing it is about the same speed as using all when the query was 10 items. It gets slower as the query gets bigger though. – John La Rooy Oct 11 '09 at 22:43
  • 1
    I've posted some of my tests as an answer. http://stackoverflow.com/questions/1285911/python-how-do-i-check-that-multiple-keys-are-in-a-dict-in-one-go/1552005#1552005 – John La Rooy Oct 11 '09 at 22:56
  • 36
    `if {'foo', 'bar'} <= set(myDict): ...` – Boris Raicheff Feb 19 '14 at 09:59
  • 21
    For anyone wondering why this works: the operator <= is the same as use .set issubset() method: https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset – edepe Oct 25 '17 at 00:24
  • {'foo','bar'} <= myDict.keys() – Rafael Beckel Feb 10 '20 at 00:25
  • 1
    @BorisRaicheff please clarify how is this better? – ImtiazeA Oct 02 '21 at 06:17
47

Simple benchmarking rig for 3 of the alternatives.

Put in your own values for D and Q


>>> from timeit import Timer
>>> setup='''from random import randint as R;d=dict((str(R(0,1000000)),R(0,1000000)) for i in range(D));q=dict((str(R(0,1000000)),R(0,1000000)) for i in range(Q));print("looking for %s items in %s"%(len(q),len(d)))'''

>>> Timer('set(q) <= set(d)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632499
0.28672504425048828

#This one only works for Python3
>>> Timer('set(q) <= d.keys()','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632084
2.5987625122070312e-05

>>> Timer('all(k in d for k in q)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632219
1.1920928955078125e-05
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
41

You don't have to wrap the left side in a set. You can just do this:

if {'foo', 'bar'} <= set(some_dict):
    pass

This also performs better than the all(k in d...) solution.

endolith
  • 25,479
  • 34
  • 128
  • 192
claytonk
  • 511
  • 4
  • 2
  • 2
    This also performs better than the all(k in d...) solution. I suggested this as an edit, but it was rejected on grounds it was [better to add a comment](http://stackoverflow.com/review/suggested-edits/6905833). So here's me doing just that – miraculixx Apr 12 '15 at 14:35
  • @miraculixx It's not better to add a comment. It's better to edit relevant information into an answer and delete the comments. – endolith Mar 13 '16 at 16:16
  • 1
    @endolith I agree, some people obviously don't as you can see in the rejected edit that I did in the first place. Anyway that's a discussion for meta not for here. – miraculixx Mar 13 '16 at 23:01
  • Can someone explain this please? I've gathered that {} creates a set, but how is the less-than-or-equal operator working here? – Locane Oct 14 '16 at 18:03
  • 1
    @Locane The <= operator tests if the first set is a subset of the second set. You could also do {'foo', 'bar'}.issubset(somedict). The documentation for set methodology can be found here: https://docs.python.org/2/library/sets.html – Meow Jan 05 '17 at 18:35
27

Using sets:

if set(("foo", "bar")).issubset(foo):
    #do stuff

Alternatively:

if set(("foo", "bar")) <= set(foo):
    #do stuff
Karl Voigtland
  • 7,637
  • 34
  • 29
23

This should work:

if all(key in foo for key in ["foo","bar"]):
    # do stuff
    pass

Hint:

Using square brackets inside all() to make a list comprehension:

if all([key in foo for key in ["foo","bar"]]):

Is not only unnecessary, but it is positively harmful, as they impede the normal short-circuiting behavior of all().

MattDMo
  • 100,794
  • 21
  • 241
  • 231
Greg
  • 5,656
  • 3
  • 22
  • 19
12

While I like Alex Martelli's answer, it doesn't seem Pythonic to me. That is, I thought an important part of being Pythonic is to be easily understandable. With that goal, <= isn't easy to understand.

While it's more characters, using issubset() as suggested by Karl Voigtland's answer is more understandable. Since that method can use a dictionary as an argument, a short, understandable solution is:

foo = {'foo': 1, 'zip': 2, 'zam': 3, 'bar': 4}

if set(('foo', 'bar')).issubset(foo):
    #do stuff

I'd like to use {'foo', 'bar'} in place of set(('foo', 'bar')), because it's shorter. However, it's not that understandable and I think the braces are too easily confused as being a dictionary.

Mr. Lance E Sloan
  • 3,297
  • 5
  • 35
  • 50
  • 2
    I think it's understandable once you understand what it means. – Bobort Oct 05 '17 at 15:19
  • It's [in the documentation](https://docs.python.org/3.7/library/stdtypes.html#frozenset.issubset) as a synonym for `.issubset()`. I think being in Python documentation makes it Pythonic by default. – ingyhere May 10 '20 at 21:02
  • My preferred one is: `if {'foo', 'bar'}.issubset(foo):` – Kiruahxh Mar 02 '22 at 10:06
12

I think this is the smartest and most Pythonic.

{'key1','key2'} <= my_dict.keys()
ingyhere
  • 11,818
  • 3
  • 38
  • 52
Shota Tamura
  • 261
  • 3
  • 12
10

check for existence of all keys in a dict:

{'key_1', 'key_2', 'key_3'} <= set(my_dict)

check for existence of one or more keys in a dict:

{'key_1', 'key_2', 'key_3'} & set(my_dict)
Naveen Reddy Marthala
  • 2,622
  • 4
  • 35
  • 67
4

Alex Martelli's solution set(queries) <= set(my_dict) is the shortest code but may not be the fastest. Assume Q = len(queries) and D = len(my_dict).

This takes O(Q) + O(D) to make the two sets, and then (one hopes!) only O(min(Q,D)) to do the subset test -- assuming of course that Python set look-up is O(1) -- this is worst case (when the answer is True).

The generator solution of hughdbrown (et al?) all(k in my_dict for k in queries) is worst-case O(Q).

Complicating factors:
(1) the loops in the set-based gadget are all done at C-speed whereas the any-based gadget is looping over bytecode.
(2) The caller of the any-based gadget may be able to use any knowledge of probability of failure to order the query items accordingly whereas the set-based gadget allows no such control.

As always, if speed is important, benchmarking under operational conditions is a good idea.

John Machin
  • 81,303
  • 11
  • 141
  • 189
  • 1
    The generator was faster for all the cases I tried. http://stackoverflow.com/questions/1285911/python-how-do-i-check-that-multiple-keys-are-in-a-dict-in-one-go/1552005#1552005 – John La Rooy Oct 11 '09 at 22:57
3

You can use .issubset() as well

>>> {"key1", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
True
>>> {"key4", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
False
>>>
Sinan Cetinkaya
  • 427
  • 5
  • 18
2

short and sweet

{"key1", "key2"} <= {*dict_name}

Chandan Kumar
  • 1,066
  • 10
  • 16
1

How about using lambda?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
Jinuk Kim
  • 765
  • 5
  • 5
  • 2
    This answer is the only functionally-correct one that will work on Python 1.5 with a simple change (s/True/1/) ... but it's got nothing else going for it. AND the True thingy would be better as the optional initializer arg rather than crammed into the front of the sequence arg. – John Machin Aug 17 '09 at 03:51
1

In case you want to:

  • also get the values for the keys
  • check more than one dictonary

then:

from operator import itemgetter
foo = {'foo':1,'zip':2,'zam':3,'bar':4}
keys = ("foo","bar") 
getter = itemgetter(*keys) # returns all values
try:
    values = getter(foo)
except KeyError:
    # not both keys exist
    pass
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
1

Not to suggest that this isn't something that you haven't thought of, but I find that the simplest thing is usually the best:

if ("foo" in foo) and ("bar" in foo):
    # do stuff
Jason Baker
  • 192,085
  • 135
  • 376
  • 510
1
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
... 
yes

Jason, () aren't necessary in Python.

Juanjo Conti
  • 28,823
  • 42
  • 111
  • 133
  • 3
    Still they might be good style... without them, my C++-addled brain always wonders if it's going to be interpreted as "if 'foo in (foo and 'bar') in foo:" – Jeremy Friesner Aug 17 '09 at 04:24
  • 1
    I understand that they aren't necessary. I just feel that they add clarity in this case. – Jason Baker Aug 17 '09 at 11:08
0

Just my take on this, there are two methods that are easy to understand of all the given options. So my main criteria is have very readable code, not exceptionally fast code. To keep code understandable, i prefer to given possibilities:

  • var <= var2.keys()
  • var.issubset(var2)

The fact that "var <= var2.keys()" executes faster in my testing below, i prefer this one.

import timeit

timeit.timeit('var <= var2.keys()', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"}')
0.1745898080000643

timeit.timeit('var.issubset(var2)', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"};')
0.2644960229999924
PietjePuk
  • 1
  • 2
0

In the case of determining whether only some keys match, this works:

any_keys_i_seek = ["key1", "key2", "key3"]

if set(my_dict).intersection(any_keys_i_seek):
    # code_here
    pass

Yet another option to find if only some keys match:

any_keys_i_seek = ["key1", "key2", "key3"]

if any_keys_i_seek & my_dict.keys():
    # code_here
    pass
ingyhere
  • 11,818
  • 3
  • 38
  • 52
0

Another option for detecting whether all keys are in a dict:

dict_to_test = { ... }  # dict
keys_sought = { "key_sought_1", "key_sought_2", "key_sought_3" }  # set

if keys_sought & dict_to_test.keys() == keys_sought: 
    # True -- dict_to_test contains all keys in keys_sought
    # code_here
    pass
ingyhere
  • 11,818
  • 3
  • 38
  • 52
0

Here's an alternative solution in case you want to get the items that didn't match...

not_existing_keys = [item for item in ["foo","bar"] if item not in foo]
if not_existing_keys:
  log.error('These items are missing', not_existing_keys)
Jacobski
  • 741
  • 6
  • 10
0
my_dict = {
    'name': 'Askavy',
    'country': 'India',
    'age': 30
}

if set(('name', 'country','age')).issubset(my_dict.keys()):
     print("All keys are present in the dictionary") 
else: 
    print("All keys are not present in  the dictionary") 
Avnish Jayaswal
  • 161
  • 1
  • 4
0

To me, simple and easy with None key in the middle with pydash ref

import pydash as _
_.get(d, 'key1.key2.key3.whatevermaybeNone.inthemiddle', default=None) )
Nam G VU
  • 33,193
  • 69
  • 233
  • 372
0

If the reason you are checking for the keys is to make sure you can access them, one option is to not check - just try to access them and then do the needful if necessary.

foo = {
    'bar': 4
}

try:
    alpha = foo['bar']
    beta = foo['leroy_jenkins']
except KeyError:
    pass # Or some other thing if you don't have the keys you expected.

keithpjolley
  • 2,089
  • 1
  • 17
  • 20
-4
>>> ok
{'five': '5', 'two': '2', 'one': '1'}

>>> if ('two' and 'one' and 'five') in ok:
...   print "cool"
... 
cool

This seems to work

  • This is clever and I was convinced it didn't work until I tried it myself. I suspected the `()` would be evaluated first and result in `True`, which would then check if `True in ok`. How does this actually work?! – durden2.0 Apr 01 '16 at 12:28
  • 8
    ('two' and 'one' and 'five') returns 'five', so it actually checks only if 'five' is on the dict – HardQuestions Apr 24 '16 at 19:30