Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonic way of accessing recursively nested dict [duplicate]

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...

like image 998
wim Avatar asked Jul 22 '14 14:07

wim


2 Answers

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
like image 95
dano Avatar answered Oct 13 '22 02:10

dano


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'
like image 41
Gerrat Avatar answered Oct 13 '22 01:10

Gerrat