Suppose I have a dictionary that is nested arbitrarily:
d = {
11: {
21: {31: 'a', 32: 'b'},
22: {31: 'a', 34: 'c'},
},
12: {
1: {2: 3}
}
}
And a list of keys whose position tells me which nested dictionary to look for every key in:
keys = [11, 21, 31]
# keys = [11, 23, 44]
Is there a simple one liner to do this? I've looked at the questions listed below, and they are similar, but not really what I'm looking for. I have also attempted it myself and came up with this:
from functools import reduce
def lookup(d, key):
return d.get(key, {}) if d and isinstance(d, dict) else None
def fn(keys, d):
return reduce(lookup, keys, d)
print(fn(keys, d)) # prints 'a'
The problem with this, is that in case of second list of keys (see commented out keys), it continues looking up nested keys further, even though the higher level key wasn't found, and continuing is pointless. How can I stop reduce
as soon as I find a final match or fail (one of the questions listed below addresses it, but I can't really apply it in my use case... or can I?)? Any other ideas? Oh and I want to accomplish this using official python libraries only. So no numpy
, pandas
etc, but functools
, itertools
are fine
Python: Convert list to dict keys for multidimensional dict with exception handling
Is there a simple one-liner for accessing each element of a nested dictioanry in Python?
Accessing nested values in nested dictionaries in Python 3.3
Using itertools for recursive function application
Stopping a Reduce() operation mid way. Functional way of doing partial running sum
Finding a key recursively in a dictionary
Thanks!
You can use functools.reduce()
:
from functools import reduce # In Python 2, don't import it. (It's a built-in)
print(reduce(dict.get, keys, d))
# 'a'
For the keys you mentioned, it goes like this:
dict.get
with d
(initial) and the first item of keys
( 11
) to get d[11]
dict.get
with the result (a dictionary) and the next item in keys
( 21
) to get {...}[21]
dict.get
... until keys
is "reduced" to the final value ( 'a'
)
Edit : As dict.get
results in None
if there is no such key, there might be undesired results. If you want to have a KeyError
, you can use operator.getitem
instead.
Here's a solution I came up with that also gives back useful information when given an invalid lookup path, and allows you to dig through arbitrary json, including nested list and dict structures. (Sorry it's not a one-liner).
def get_furthest(s, path):
'''
Gets the furthest value along a given key path in a subscriptable structure.
subscriptable, list -> any
:param s: the subscriptable structure to examine
:param path: the lookup path to follow
:return: a tuple of the value at the furthest valid key, and whether the full path is valid
'''
def step_key(acc, key):
s = acc[0]
if isinstance(s, str):
return (s, False)
try:
return (s[key], acc[1])
except LookupError:
return (s, False)
return reduce(step_key, path, (s, True))
d = {
11: {
21: {
31: 'a from dict'
},
},
}
l = [None] * 50
l[11] = [None] * 50
l[11][21] = [None] * 50
l[11][21][31] = 'a from list'
from functools import reduce
goodkeys = [11, 21, 31]
badkeys = [11, 12, 13]
print("Reducing dictionary (good):", reduce(lambda c,k: c.__getitem__(k), goodkeys, d))
try:
print("Reducing dictionary (bad):", reduce(lambda c,k: c.__getitem__(k), badkeys, d))
except Exception as ex:
print(type(ex), ex)
print("Reducing list (good):", reduce(lambda c,k: c.__getitem__(k), goodkeys, l))
try:
print("Reducing list (bad):", reduce(lambda c,k: c.__getitem__(k), badkeys, l))
except Exception as ex:
print(type(ex), ex)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.