views:

694

answers:

10

I want to do something like:

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

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

I'm not sure if its possible but would like to know. :-)

+7  A: 

How about

if all([key in foo for key in ["foo","bar"]]):
    #do stuff
    pass
Greg
The square brackets there are unnecessary..
John Fouhy
indeed, not only unnecessary, positively harmful, as they impede the normal short-circuiting behavior of `all`.
Alex Martelli
+16  A: 

Well, you could do this:

>>> if all (k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!
hughdbrown
+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
I love all() and any(). They make so many algorithms so much cleaner.
hughdbrown
I ultimately ended up using this solution. It seemed the best for larger datasets. When checking for say 25 or 30 keys.
Frederick Reeve
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
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
A: 

How about using lambda?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
rein
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
+14  A: 
if set(("foo", "bar")) <= set(myDict): ...
Alex Martelli
Satisfying on many levels...
Jarret Hardie
Python pulled me away from Java. I have to ask myself what kind of language would pull me away from Python. :)
FogleBird
+1 This is the most beautiful thing I've seen today
mhawke
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
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.
gnibbler
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
gnibbler
+4  A: 

Using sets:

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

Alternatively:

if set(("foo", "bar")) <= set(foo):
    #do stuff
Karl Voigtland
set(d) as I used in my answer is just like set(d.keys()) but faster, shorter, and I would say stylistically preferable.
Alex Martelli
`set(d)` is the same as `set(d.keys())` ( without the intermediate list that `d.keys()` constructs )
THC4k
agreed, thanks
Karl Voigtland
A: 

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
THC4k
A: 

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
Sure, I like simplicity too!, and that's fine for just two keys as in the example, but it gets wordy and unwieldy fast for the general case of "multiple" keys as per the title.
Alex Martelli
A: 
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
... 
yes

Jason, () aren't necessary in Python.

Juanjo Conti
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
I understand that they aren't necessary. I just feel that they add clarity in this case.
Jason Baker
A: 

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
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
gnibbler
+2  A: 

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
gnibbler