Reputation: 2148
I have an API that I call that returns a dictionary. Part of that dictionary is itself another dictionary. In that inside dictionary, there are some keys that might not exist, or they might. Those keys could reference another dictionary.
To give an example, say I have the following dictionaries:
dict1 = {'a': {'b': {'c':{'d':3}}}}
dict2 = {'a': {'b': {''f': 2}}}
I would like to write a function that I can pass in the dictionary, and a list of keys that would lead me to the 3
in dict1
, and the 2
in dict2
. However, it is possible that b
and c
might not exist in dict1
, and b
and f
might not exist in dict2
.
I would like to have a function that I could call like this:
get_value(dict1, ['a', 'b', 'c'])
and that would return a 3, or if the keys are not found, then return a default value of 0.
I know that I can use something like this:
val = dict1.get('a', {}).get('b', {}).get('c', 0)
but that seems to be quite wordy to me.
I can also flatten the dict (see https://stackoverflow.com/a/6043835/1758023), but that can be a bit intensive since my dictionary is actually fairly large, and has about 5 levels of nesting in some keys. And, I only need to get two things from the dict.
Right now I am using the flattenDict
function in the SO question, but that seems a bit of overkill for my situation.
Upvotes: 2
Views: 4220
Reputation: 36802
Without recursion, just iterate through the keys and go down one level at a time. Putting that inside a try
/except
allows you to handle the missing key case. KeyError
will be raised when the key is not there, and TypeError
will be raised if you hit the "bottom" of the dict too soon and try to apply the []
operator to an int
or something.
def get_value(d, ks):
for k in ks:
try:
d = d[k] # descend one level
except (KeyError, TypeError):
return 0 # when any lookup fails, return 0
return d # return the final element
Upvotes: 3
Reputation: 10298
You can use a recursive function:
def get_value(mydict, keys):
if not keys:
return mydict
if keys[0] not in mydict:
return 0
return get_value(mydict[keys[0]], keys[1:])
If keys can not only be missing, but be other, non-dict types, you can handle this like so:
def get_value(mydict, keys):
if not keys:
return mydict
key = keys[0]
try:
newdict = mydict[key]
except (TypeError, KeyError):
return 0
return get_value(newdict, keys[1:])
Upvotes: 3
Reputation: 117866
Here is a recursive function that should work for general cases
def recursive_get(d, k):
if len(k) == 0:
return 0
elif len(k) == 1:
return d.get(k[0], 0)
else:
value = d.get(k[0], 0)
if isinstance(value, dict):
return recursive_get(value, k[1:])
else:
return value
It takes arguments of the dict
to search, and a list of keys, which it will check one per level
>>> dict1 = {'a': {'b': {'c':{'d':3}}}}
>>> recursive_get(dict1, ['a', 'b', 'c'])
{'d': 3}
>>> dict2 = {'a': {'b': {'f': 2}}}
>>> recursive_get(dict2, ['a', 'b', 'c'])
0
Upvotes: 0