Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep set python dictionary

I am having a saved list of Python dict keys:

['level_one', 'level_two', 'test']

These are the keys of a dictionary:

mydict = {
    'level_one' : {
        'level_two' : {
            'test' : "Hello World"
        }
    }
}

Normally I could set the test key like this:

mydict['level_one']['level_two']['test'] = "Hello StackOverflow"

Unfortunately, the list/dict are generated on the fly, and I don't know how deep it can go. Is there a possibility to update the dict key by the list of keys without using braces?

I only was able to reflect this functionality for getting the string:

def deepGet(sourceDict, *keys):
    return reduce(lambda d, k: d.get(k) if d else None, keys, sourceDict)

> deepGet(mydict, *['level_one', 'level_two', 'test'])
>> Hello World

Not a duplicate. This is regarding setting, not getting of nested dictionary.

like image 850
RA. Avatar asked Aug 14 '17 00:08

RA.


2 Answers

There is now a way to deep set values dynamically, without changing the container type to a defaultdict or some other dynamically creating type: glom.assign().

A usage example with your case:

import glom

target = {}
path = 'level_one.level_two.test'

glom.assign(target, path, 'hello world', missing=dict)
# {'level_one': {'level_two': {'test': 'hello world'}}}

Notice we passed the missing=dict, telling glom to create missing keys with the built-in dict constructor. You'll also need to pip install glom, but it's pure-Python and compatible with Python 2, 3, and PyPy.

There's a lot more you can do with glom, especially around deep getting and setting. I should know, since (full disclosure) I created it. That means if you find a gap, you should let me know!


like image 135
Mahmoud Hashemi Avatar answered Sep 24 '22 15:09

Mahmoud Hashemi


We need a defaultdict that will keep making new instances of itself all the way down.

import collections

recursive_dict = lambda: collections.defaultdict(recursive_dict)

mydict = recursive_dict()

At this point, you can simplify your deepGet to just use operator.getitem instead of the lambda you have now. operator.getitem is "shorthand" for lambda a, b: a[b].

As for setting a key, you can just write a loop:

keys = ['level_one', 'level_two', 'test']
d = mydict
for key in keys[:-1]:
    d = d[key]
d[keys[-1]] = 'Hello World'

If dictionaries are missing, the defaultdict will silently create them, no need to check for them first.

Converting this to a reduce() call is left as an exercise for the reader.

like image 44
Kevin Avatar answered Sep 25 '22 15:09

Kevin