Filter dictionaries and nested dictionaries for key
Question:
I want to solve the following problem without introducing several if/else if cases. I have the following dictionaries:
dict_1 = {"full_name": {"name": "Foo", "surname": "Bar"}, "age": 29, "test": 0}
dict_2 = {"name": "Foo", "age": 29, "test": 1}
Now, I want to receive the value of the key "name" for both dictionaries. In the "real" program situation, I don’t know if a structure of dict_1
or dict_2
will be present, but in all structures that will be present, the key "name" will exist. So I need a function like the following:
filter_dict_for_key(dict, key):
...
return val(key)
For both dictionaries, I expect the output "Foo".
Is there an elegant pythonic way to do this with filter/dict comprehension, etc.?
Answers:
A recursive function is usually the easiest way to handle an arbitrarily nested object. Since there’s always the possibility of there being more (or fewer) than one matching item, this function returns a list of matches:
>>> def get_name_nested(d):
... """Return a list of all 'name' values in a nested dict."""
... if not isinstance(d, dict):
... return []
... if 'name' in d:
... return [d['name']]
... return [n for s in d.values() for n in get_name_nested(s)]
...
>>> get_name_nested({"full_name": {"name": "Foo", "surname": "Bar"}, "age": 29, "test": 0})
['Foo']
>>> get_name_nested({"name": "Foo", "age": 29, "test": 1})
['Foo']
If you need to handle additional types of nesting (e.g. a list of dicts or a list of lists which may themselves be nested), it’s pretty straightforward to extend this approach to handle those.
You can use the default argument to dict.get()
name = mydict.get('name', mydict.get('full_name', {}).get('name'))
So if there’s no name
key in the top-level, it looks in the full_name
key. You have to use .get()
for the default in case there’s no full_name
key.
Or use a conditional expression:
name = mydict['name'] if 'name' in mydict else mydict['full_name']['name']
Note that this will get an error if neither key exists. You can use try/except
to catch that if necessary.
I want to solve the following problem without introducing several if/else if cases. I have the following dictionaries:
dict_1 = {"full_name": {"name": "Foo", "surname": "Bar"}, "age": 29, "test": 0}
dict_2 = {"name": "Foo", "age": 29, "test": 1}
Now, I want to receive the value of the key "name" for both dictionaries. In the "real" program situation, I don’t know if a structure of dict_1
or dict_2
will be present, but in all structures that will be present, the key "name" will exist. So I need a function like the following:
filter_dict_for_key(dict, key):
...
return val(key)
For both dictionaries, I expect the output "Foo".
Is there an elegant pythonic way to do this with filter/dict comprehension, etc.?
A recursive function is usually the easiest way to handle an arbitrarily nested object. Since there’s always the possibility of there being more (or fewer) than one matching item, this function returns a list of matches:
>>> def get_name_nested(d):
... """Return a list of all 'name' values in a nested dict."""
... if not isinstance(d, dict):
... return []
... if 'name' in d:
... return [d['name']]
... return [n for s in d.values() for n in get_name_nested(s)]
...
>>> get_name_nested({"full_name": {"name": "Foo", "surname": "Bar"}, "age": 29, "test": 0})
['Foo']
>>> get_name_nested({"name": "Foo", "age": 29, "test": 1})
['Foo']
If you need to handle additional types of nesting (e.g. a list of dicts or a list of lists which may themselves be nested), it’s pretty straightforward to extend this approach to handle those.
You can use the default argument to dict.get()
name = mydict.get('name', mydict.get('full_name', {}).get('name'))
So if there’s no name
key in the top-level, it looks in the full_name
key. You have to use .get()
for the default in case there’s no full_name
key.
Or use a conditional expression:
name = mydict['name'] if 'name' in mydict else mydict['full_name']['name']
Note that this will get an error if neither key exists. You can use try/except
to catch that if necessary.