How can we do recursive access of nested dicts, in the general case?
from collections import defaultdict
D = lambda: defaultdict(D)
d = D()
keys = ['k1', 'k2', 'k3']
value = 'v'
if len(keys) == 3:
k1, k2, k3 = keys
d[k1][k2][k3] = value
else:
???
I was going down some horrible path of reduce
, d.__getitem__
and d.__setitem__
but felt there must surely be a more elegant way...
It's pretty ugly, but this works:
def set_val(d, keys, val):
reduce(lambda x,y: x[y], keys[:-1], d)[keys[-1]] = val
Slightly more readable version:
def set_val(d, keys, val):
last = keys[-1] # Key we want to set val on
search_keys = keys[:-1] # Keys we need to traverse
reduce(lambda x,y: x[y], search_keys, d)[last] = val
Usage:
>>> from collections import defaultdict
>>> D = lambda: defaultdict(D)
>>> d = D()
>>> set_val(d, ['k1', 'k2', 'k3'], "hi")
>>> d
defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k1': defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k2': defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k3': 'hi'})})})
>>> d['k1']['k2']['k3']
'hi'
It uses reduce
to reach the inner-most dict requested (keys[:-1]
), then sets the final key in the list to the desired value (output_of_reduce[keys[-1]] = val
).
Note that in Python 3 you'd need a from functools import reduce
to use this.
Here's the code expanded for clarity:
def set_val(d, keys, val):
out = d
for k in keys[:-1]:
out = out[k]
out[keys[-1]] = val
You could just use recursion. Can't say its any more elegant or more pythonic than looping, or using reduce though.
def assign(dct, keylist, value):
if not keylist:
dct = value
else:
dct[keylist[0]] = assign(dct[keylist[0]], keylist[1:], value)
return dct
if __name__ == '__main__':
from collections import defaultdict
D = lambda: defaultdict(D)
d = D()
keys = ['k1', 'k2', 'k3']
value = 'v'
assign(d, keys, value)
print d['k1']['k2']['k3']
[prints] 'v'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With