109

I have a dictionary that looks like that:

grades = {
        'alex' : 11,
        'bob'  : 10,
        'john' : 14,
        'peter': 7
       }

and a list of names students = ('alex', 'john')

I need to check that all the names in students exist as keys in grades dict.

grades can have more names, but all the names in students should be in grades

There must be a straightforward way to do it, but i'm still new to python and can't figure it out. tried if students in grades, didn't work.

In the actual cases, the lists will be much bigger.

martineau
  • 119,623
  • 25
  • 170
  • 301
applechief
  • 6,615
  • 12
  • 50
  • 70

4 Answers4

253

Use all():

if all(name in grades for name in students):
    # whatever
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Great! Any shortcut way to get the elements that are missing from the dic if the condition is False? – gdvalderrama Aug 31 '17 at 11:34
  • 7
    @guival Yup, you can use set operations, e.g. `set(students) - grades.keys()` in Python 3. – Sven Marnach Aug 31 '17 at 13:17
  • is there a way to check if any of a list of keys is a sub string in a string? – Jonathan Sep 07 '17 at 18:28
  • @Jonathan Not sure what you mean, maybe `any(k in my_string for k in keys)`? – Sven Marnach Sep 07 '17 at 18:30
  • @SvenMarnach just discovered any()! That's exactly what I was looking for :) – Jonathan Sep 07 '17 at 18:32
  • This is offtopic, but why do "all" and generator expression share a parenthesis? If we remove all() casting, generator is invalid, due to missing parenthesis... – yomajo Jun 01 '23 at 12:50
  • 1
    @yomajo It's just how Python's grammer is defined. Generator expressions need to be in parens, but the parens of a single-argument function call will do. If you pass a generator expression to a function with two arguments, you need an additional pair of parens. – Sven Marnach Jun 01 '23 at 13:08
30
>>> grades = {
        'alex' : 11,
        'bob'  : 10,
        'john' : 14,
        'peter': 7
}
>>> names = ('alex', 'john')
>>> set(names).issubset(grades)
True
>>> names = ('ben', 'tom')
>>> set(names).issubset(grades)
False

Calling it class is invalid so I changed it to names.

Ben Liyanage
  • 4,993
  • 1
  • 21
  • 23
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • 1
    This can't short-cut, in contrast to `all()`. It will always be O(m+n), where m and n are the respective sizes of `names` and `grades`. Using `all()` will be O(m), and might short-cut. – Sven Marnach Jun 12 '12 at 10:56
  • 8
    @SvenMarnach Right, I'll just leave it here since it is another approach but I agree that yours is the best. – jamylak Jun 12 '12 at 10:58
  • 2
    Definitely leave it here! It's an interesting approach in any case. – Sven Marnach Jun 12 '12 at 10:59
6

Assuming students as set

if not (students - grades.keys()):
    print("All keys exist")

If not convert it to set

if not (set(students) - grades.keys()):
    print("All keys exist")
abhilekh
  • 101
  • 1
  • 5
6

You can test if a number of keys are in a dict by taking advantage that <dict>.keys() returns a set.

This logic in code...

if 'foo' in d and 'bar' in d and 'baz' in d:
    do_something()

can be represented more briefly as:

if {'foo', 'bar', 'baz'} <= d.keys():
    do_something()

The <= operator for sets tests for whether the set on the left is a subset of the set on the right. Another way of writing this would be <set>.issubset(other).

There are other interesting operations supported by sets: https://docs.python.org/3.8/library/stdtypes.html#set

Using this trick can condense a lot of places in code that check for several keys as shown in the first example above.

Whole lists of keys could also be checked for using <=:

if set(students) <= grades.keys():
    print("All studends listed have grades in your class.")

# or using unpacking - which is actually faster than using set()
if {*students} <= grades.keys():
    ...

Or if students is also a dict:

if students.keys() <= grades.keys():
    ...
Todd
  • 4,669
  • 1
  • 22
  • 30