Reputation: 20470
Is there an easy, concise way to get a value from a nested dict and get None
if it's not there?
d1 = None
d2 = {}
d3 = {"a": {}}
d4 = {"a": {"b": 12345}}
ds = [d1, d2, d3, d4]
def nested_get(d):
# Is there a simpler concise one-line way to do exactly this, query a nested dict value, and return None if
# it doesn't exist?
a_val = d.get("a") if d else None
b_val = a_val.get("b") if a_val else None
return b_val
if __name__ == "__main__":
bs = [nested_get(d) for d in ds]
print("bs={}".format(bs))
Upvotes: 2
Views: 313
Reputation: 498
If you'd rather specify your "path" into the nested dict as a string path (like a directory) rather than a set of individual keys, this has worked for our needs:
def deep_get(d, path, default=None):
"""Get a deeply nested value in a dict based on multiple keys
:param d: The dictionary
:param path: The key path to search separated by '/'
:param default: The default value to return if there is nothing
:returns: The value at the given path or the default
"""
dd = d
split = path.split("/")
for i, key in enumerate(split):
try:
dd = dd.get(key, {})
except AttributeError as exc:
p = "/".join(split[0:i])
raise AttributeError("{} at {}".format(exc.message, p)), \
None, sys.exc_info()[2]
return dd or default
where the usage is as simple as
d = {
'a': {
'b': {
'c': {
'd': "hi!"
}
}
}
}
print deep_get(d, 'a/b/c/d')
Upvotes: 0
Reputation: 9008
If you want to get a little dirty, you can create a custom class that extends how a typical dictionary handles subscripting.
class QueryDict(dict):
def __getitem__(self, keys):
current = self
try:
for key in keys:
current = dict.__getitem__(current, key)
return current
except (TypeError, KeyError):
return None
d = {"a": {"b": {"c": 12345}}}
d = QueryDict(d)
print d['a','b','c'] # 12345
print d['a,c,e'] # None
Or, if you're trying to dynamically call entries, it might be better to allow keys to be passed in by a comma-separated string.
class QueryDict(dict):
def __getitem__(self, key_string):
current = self
try:
for key in key_string.split(','):
current = dict.__getitem__(current, key)
return current
except (TypeError, KeyError):
return None
d = {"a": {"b": {"c": 12345}}}
d = QueryDict(d)
print d['a,b,c'] # 12345.
print d['a,c,e'] # None
Upvotes: 1
Reputation: 20470
OK, using a simple custom function, I can do this, as a general concise solution:
d1 = None
d2 = {}
d3 = {"a": {}}
d4 = {"a": {"b": 12345}}
ds = [d1, d2, d3, d4]
def get_nested_dict_value(d, *args):
for a in args:
d = d.get(a) if d else None
return d
if __name__ == "__main__":
bs = [get_nested_dict_value(d, "a", "b") for d in ds]
print("bs={}".format(bs))
Upvotes: 1
Reputation: 3731
This should work:
def nested_get(d, *keys):
for key in keys:
if d is None:
break
d = d.get(key)
return d
Upvotes: 1