15

For a large list of nested dictionaries, I want to check if they contain or not a key. Each of them may or may not have one of the nested dictionaries, so if I loop this search through all of them raises an error:

for Dict1 in DictionariesList:
     if "Dict4" in Dict1['Dict2']['Dict3']:
         print "Yes"

My solution so far is:

for Dict1 in DictionariesList:    
    if "Dict2" in Dict1:
        if "Dict3" in Dict1['Dict2']:
            if "Dict4" in Dict1['Dict2']['Dict3']:
                print "Yes"

But this is a headache, ugly, and probably not very resources effective. Which would be the correct way to do this in the first type fashion, but without raising an error when the dictionary doesnt exist?

martineau
  • 119,623
  • 25
  • 170
  • 301
I want badges
  • 6,155
  • 5
  • 23
  • 38

4 Answers4

43

Use .get() with empty dictionaries as defaults:

if 'Dict4' in Dict1.get('Dict2', {}).get('Dict3', {}):
    print "Yes"

If the Dict2 key is not present, an empty dictionary is returned, so the next chained .get() will also not find Dict3 and return an empty dictionary in turn. The in test then returns False.

The alternative is to just catch the KeyError:

try:
    if 'Dict4' in Dict1['Dict2']['Dict3']:
        print "Yes"
except KeyError:
    print "Definitely no"
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 3
    Amazing, tested and works, very much appreciated. Will accept asap. U r truly a Ninja. – I want badges Sep 02 '13 at 17:28
  • 2
    The first proposal will fail if there's any `None`-valued keys in the chain. E.g., the test will not work for `Dict1 = {'Dict2': None}`. So it seems that catching the exception is the cleanest solution. – Alex O May 11 '17 at 13:12
  • 1
    @AlexO: it'll fail for any object that doesn't have a `.get()` method, yes. It is not the job of this code to account for every possibility; unless you explicitly need to support different value types, do not catch that exception as that indicates a bug elsewhere. – Martijn Pieters May 11 '17 at 13:14
  • Thanks for pointing that out (I'm currently switching from Perl, looking for the Python-equivalent of a `defined($Dict1{'Dict2'}{'Dict3'})` expression : ) – Alex O May 11 '17 at 13:22
8

How about a try/except block:

for Dict1 in DictionariesList:
    try:
        if 'Dict4' in Dict1['Dict2']['Dict3']:
            print 'Yes'
    except KeyError:
        continue # I just chose to continue.  You can do anything here though
6

Here's a generalization for an arbitrary number of keys:

for Dict1 in DictionariesList:
    try: # try to get the value
        reduce(dict.__getitem__, ["Dict2", "Dict3", "Dict4"], Dict1)
    except KeyError: # failed
        continue # try the next dict
    else: # success
        print("Yes")

Based on Python: Change values in dict of nested dicts using items in a list.

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
0

Use a NestedDict. First install ndicts

pip install ndicts

Here you have a list of nested dictionaries.

li = [
    {'k1': {'k2': None, 'k3': None}}, 
    {'k1': {'k2': None, 'k3': None}, 'k4': None}, 
    {'k1': {'k2': None, 'k3': None, 'k4': None}}
]

Say we want to print a yes whenever 'k4' is present

>>> from ndicts.ndicts import NestedDict
>>> for nested_dict in li:
...     nd = NestedDict(nested_dict)
...         for key in nd:
...             if 'k4' in key:
...                 print('yes')
yes
yes
edd313
  • 1,109
  • 7
  • 20