Iterate over nested keys in Python dict and break on first occurence

Question:

I have a JSON dict like the following :

             "{
                        "a":1,
                        "b":{
                             "b1":False,
                             "b2":{"b21": 2, "b22":8}
                         },  
                        "c": { 
                            "b1":True, 
                            "b2":2,
                            "b4":8
                         },
                        "d":{
                            "b1":False, 
                            "d1":89
                         }
                       }"

I want to check the value of the key "b1" in the dictionary, and get out when I find b1=True. If I check the entire dict (nested keys included), and I don’t find b1=True, then I would like to return False. For the example above, my function should return True.

Basically I want to break the code on the first occurrence of b1=True and iterate over all the keys of the dict (in all levels), and if this occurrence does not exist, return False.

This is what I came up with :

def isb1True(jsonDoc):
  found = False
    for (key,value) in jsonDoc.iteritems():
        if key=='b1':
            if value==True :
                found=True
                break
        else:
            isb1True(value)
   return found

My code always returns False.

Asked By: Samiella

||

Answers:

You need to return from the recursive calls too, and use that to inform wether or not you are going to continue looping; your code ignores what the recursive isb1True(value) call returns.

You can use the any() function to short-circuit testing recursive values:

def isb1true(d):
    if not isinstance(d, dict): return False
    return any(v if k == 'b1' else isb1true(v) for k, v in d.iteritems())

The above recurses for any key that is not 'b1', and recursion stops when that value is not a dictionary (in which case it won’t be b1 so that result is not a 'b1': True case).

I’m assuming that 'b1' is always set to a boolean; the above returns True for any ‘truthy’ value for that key.

Some test cases:

>>> isb1true({'b1': True})
True
>>> isb1true({'b1': False})
False
>>> isb1true({'b': {'b1': True}})
True
>>> isb1true({'b': {'b1': False}})
False
>>> isb1true({'b': {'c': True, 'spam': 'eggs', 'ham': {'bar': 'baz', 'b1': True}}})
True
>>> isb1true({'b': {'c': True, 'spam': 'eggs', 'ham': {'bar': 'baz'}}})
False
Answered By: Martijn Pieters

If you use a NestedDict it becomes a simple task.

from ndicts import NestedDict

def contains(d, k):
    nd = NestedDict(d)
    for key in nd:
        if k in key:
            return True
    return False
>>> d = {
        "a": 1,
        "b": {"b1": False,
              "b2": {"b21": 2, "b22": 8}
    }
>>> contains(d, "b1")
True
>>> contains(d, "asd")
False

To install ndicts pip install ndicts

Answered By: edd313
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.