Skip to content
Advertisement

Python DFS nested dictionary

I’ve written a function which should be able to search a nested dictionary, using DFS, to find a specific value. The recursive element seems to be working fine, however, when the base case should return True, it simply doesn’t.

obj = {'a': [{'c':'d'}, {'e':'f'}],
       'b': [{'g':'h'}, {'i':'j'}]}

def obj_dfs(obj, target):
  
  if type(obj) == dict:
    for key, val in obj.items():
      obj_dfs(key, target)
      obj_dfs(val, target) 
  
  elif type(obj) in (list, tuple):
    for elem in obj:
      obj_dfs(elem, target)
  else:
    if obj == target:
      print(f"{obj} == {target}")
      return True
    else:
      print(f"{obj} != {target}")  
  return False 

obj_dfs(obj, 'j')       

And the results. As you can see, the standard output “i==i” shows that this element was evaluated correctly but the return True statement isn’t functioning as intended. I’ve verified that if I call obj_dfs(obj, 'j'), that experiences the same error.

a != j
c != j
d != j
e != j
f != j
b != j
g != j
h != j
i != j
j == j
False

Why is this? And how can I fix this?

Advertisement

Answer

As the comments point out, you need to return the results of the recursive calls. Since you are just looking for a True/False match, you can pass the recursive calls into any() which will exit early with True if there is a match. The base case can simple be whether obj == target.

obj = {'a': [{'c':'d'}, {'e':'f'}],
       'b': [{'g':'h'}, {'i':'j'}]}

def obj_dfs(obj, target):
    if obj == target:
        return True

    if isinstance(obj, dict):
        return any(obj_dfs(v, target) for v in obj.items())
    
    elif isinstance(obj, (list, tuple)):
        return any(obj_dfs(l, target) for l in obj)

    return False
                   
obj_dfs(obj, 'i'), obj_dfs(obj, 'j'), obj_dfs(obj, 'd'), obj_dfs(obj, 'x')
# (True, True, True, False)

This allows for a three simple blocks. Notice we are checking for a tuple as well as a list in the last isinstance. This allows you to simply pass in the dict item()s rather than looping over keys and values independently.

Adding a print(obj) as the first line of the function will show the order in which you are traversing the data. For example obj_dfs(obj, 'j') will print:

{'a': [{'c': 'd'}, {'e': 'f'}], 'b': [{'g': 'h'}, {'i': 'j'}]}
('a', [{'c': 'd'}, {'e': 'f'}])
a
[{'c': 'd'}, {'e': 'f'}]
{'c': 'd'}
('c', 'd')
c
d
{'e': 'f'}
('e', 'f')
e
f
('b', [{'g': 'h'}, {'i': 'j'}])
b
[{'g': 'h'}, {'i': 'j'}]
{'g': 'h'}
('g', 'h')
g
h
{'i': 'j'}
('i', 'j')
i
j
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement