Home > Software engineering >  How to iterate dynamically over a dictionary
How to iterate dynamically over a dictionary

Time:06-17

How to iterate over a dictionary / JSON using a dynamic query.

For example consider the below dict

dict = {'Adam': {
            'English': {
                'Score': 99,
                'Time': 3400,
                'Classes': 4},
            'Math': {
                'Score': 45,
                'Time': 779,
                'Classes': 5}},
        'Tim': {
            'English': {
                'Score': 74,
                'Time': 12,
                'Classes': 99},
            'Math': {
                'Score': 12,
                'Time': 333,
                'Classes': 1}}
        }

I want to set the value of a given path for example

path = '/Adam/English/Score'
new_value = 87

Note that the value assigned could be another dict as well for example

path = '/Adam/English'
new_value = {'Score': 11,
             'Time': 2,
             'Classes': 9}

Any help would be useful.

Edit: Below is my attempt

keys = path.split('/')[1:]
new_data = None
for key in keys:
    if new_data is None:
        new_data = dict[key]
    else:
        new_data = new_data[key]

new_data = new_value
print(dict)

But here the dict still has the old value

CodePudding user response:

I made some assumptions, for example that '/' is not part of any dict-keys and that the path must be valid. Adjust the function as needed.

def deep_set(d, path, value):
    sep = '/'
    *trail, last = path.strip(sep).split(sep)
    
    for part in trail:
        d = d[part]
        
    d[last] = value

Demo:

>>> d = {'a': 1}
>>> deep_set(d, 'a', 2)
>>> d
{'a': 2}
>>> d = {'a': {'b': 1}}
>>> deep_set(d, 'a/b', 2)
>>> d
{'a': {'b': 2}}

edit:

Note that if there are consecutive '/' characters then the empty string will be looked up as a dict key. e.g.

'a/b//c'.split('/') -> ['a', 'b', '', 'c']

It's unclear whether you want to treat leading/trailling '/' characters as part of the path or not (in my function, they are removed with str.strip). Again, adjust as needed.

  • Related