# Get value from dictionary given a list of nested keys

I would like to get a deeply-nested value, for example {"a":{"b":{"c":"myValue"}} by providing the keys to traverse. I tried chaining together .get() but that didn't work for cases where some of the keys were missing—in those cases, I want to get None instead.

Could this solution be improved?

#!/usr/bin/env python
# encoding: utf-8

def nestedGet(d,p):
if len(p) > 1:
try:
return nestedGet(d.get(p[0]),p[1:])
except AttributeError:
return None
if len(p) == 1:
try:
return d.get(p[0])
except AttributeError:
return None

print nestedGet({"a":{"b":{"c":1}}},["a","b","c"]) #1
print nestedGet({"a":{"bar":{"c":1}}},["a","b","c"]) #None


Unless I'm missing something you'd like to implement, I would go for a simple loop instead of using recursion:

def nested_get(input_dict, nested_key):
internal_dict_value = input_dict
for k in nested_key:
internal_dict_value = internal_dict_value.get(k, None)
if internal_dict_value is None:
return None
return internal_dict_value

print(nested_get({"a":{"b":{"c":1}}},["a","b","c"])) #1
print(nested_get({"a":{"bar":{"c":1}}},["a","b","c"])) #None


If you were to solve it with a third-party package, like jsonpath-rw, the solution would be as simple as constructing the path by joining the keys with a dot and parsing the dictionary:

from jsonpath_rw import parse

def nested_get(d, path):
result = parse(".".join(path)).find(d)
return result[0].value if result else None


Some code style notes about your current approach:

• Python functions does not follow the camel case naming conventions, put an underscore between words - nested_get() instead of nestedGet()
• there should be extra spaces after the commas (PEP8 reference)
• improve on variable naming, make them more descriptive - for instance, p can be named as path
• use print() function instead of a statement for Python3.x compatibility

You can chain together dict.get() functions and use the optional default argument and specify an empty dict to return instead of None if the key doesn't exist. Except, let the last .get() return None if the key is not found.

If any of the keys to be traversed are missing, a empty dict is returned and the following .get()s in the chain will be called on the empty dict, which is fine., The final .get() in the chain will either return the value if all the keys exist or will return None as desired.

example 1:

input_dict = {"a":{"b":{"c":1}}} #1
input_dict.get("a", {}).get("b", {}).get("c") # 1


example 2:

input_dict = {"a":{"bar":{"c":1}}} #1
input_dict.get("a", {}).get("b", {}).get("c") # None