I'm sharing a working function that is able to return a value from a dictionary disregarding the depth of the nested object. For example: my_dict = {"People": [{"name": "Alice"}, {"name": "Bob"}]} If I wanted to get the value of "People" id write: my_dict["People"] (path length is 1) And to get "Bob": my_dict["People"][1] (path length is 2) **notice the difference between retrieving a value in a dict vs list.
The problem arises when you don't know ahead of time the depth (path length) of the value. I've searched for a function that does this but failed to find one. I'm posting this to share the function for anyone else trying to achieve this and is having trouble.
The following function solves this problem: Input:
- data: dictionary to search in
- path: path to the object in list format (e.g. Bob's path will be ["People", 1] **this way the depth doesn't matter and can be determined at runtime.
def get_nested_object(data: dict, path: list):
# Get outermost key from path
key = path[0]
# if data is type dictionary
if isinstance(data, dict):
# if key is found
if key in data:
# if path length == 1 -> this is the object we need to return
if len(path) == 1:
return {key: data[key]}
# else try to find value in current key
else:
return get_nested_object(data[key], path[1:])
# else if data is type list
elif isinstance(data, list):
# if path length == 1 -> this is the object we need to return
if len(path) == 1:
return data[key]
else:
return get_nested_object(data[key], path[1:])
else:
return "Invalid data"
** note that this code does not have data validation and error handling
First, your
get_nested_objectfunction is overly complicated, so I offer a simpler version without any error safeguarding. Next is the functionfind_all, which given a target value, it will find all the (path, value). Thefind_firstfunction is built on top offind_all. Finally,find_allis built on top ofiter_values, which given a nested data structure, generates all the paths and data.Output:
Update
As discussed, I did not place any error checking into:
The caller then can catch any exception which might arise:
An alternative is to redesign the function
get_nested_objectto return a default value in case of error:Then, the usages would be:
Of the two implementations, I still like the original implementation for two reasons
KeyErrorandIndexError, then the script/app will crash and we know that we have a problem. This type of error is easier to detect than the silent/non-crash errors.