Reputation: 23
I want to update the specific elements only with the given path dynamically it can be done by selecting my_dict['m1]['m2']['m3'] = 100 but i want dynamic approach
paths = ['m1.m2.m3','m1.m2.m4','ml2.2']
Dictionary
my_dict= {
'm1': {
'm2': {
'm3': 55,
'm4' : 75
}
},
'ml2': ['a', 'b', 'c']
}
for the first path (m1.m2.m3) I want to update 55 by 100 or any other number. In the dict also the last path i.e, go to the ml2 and update the 2'nd element 'b' by 'a' or any other value
Upvotes: 1
Views: 200
Reputation: 156
As you can imagine, such problem may require some sort of recursivity.
Consider the following function:
def get_nested(data, *args):
if args and data:
element = args[0]
if element:
value = data.get(element)
return value if len(args) == 1 else get_nested(value, *args[1:])
You can now get any depth in your dictionary by doing something like:
get_nested(dictionary, first_level_key, second_level_key, key_you_want)
By following the same principle, you can set (update) a dictionary at any depth like so:
def set_nested(data, to_set, *args):
if args and data:
key = args[0]
if key:
if len(args) == 1:
data[key] = to_set
else:
data[key] = set_nested(data[key], to_set, *args[1:])
return data
That works in the following example:
d = {
'a': {
'b': {
'c': 0
}
}
}
set_nested(d, 1, 'a', 'b', 'c')
# return: {'a': {'b': {'c': 1}}}
These functions give you the logic behind the recursive algorithms needed to fulfill your requirements. You can then easily modify them to add your own logic. Such as the fact of using strings instead of function list parameter.
Have a nice day
Upvotes: 1
Reputation: 15364
A very clean way of achieving this is by using the built-in functools.reduce
function:
from functools imnport reduce
results = [reduce(dict.get, [my_dict] + path.split('.')) for path in paths]
However, this works only if your dict contains only dicts, and the sub-dicts contain only dicts, and so on. Here is an example with:
>>> paths = ['m1.m2.m3','m1.m2.m4','ml2.2']
>>> my_dict= {
... 'm1': {
... 'm2': {
... 'm3': 55,
... 'm4' : 75
... }
... },
... 'ml2': {'0': 'a', '1': 'b', '2': 'c'}
... }
>>> results = [reduce(dict.get, [my_dict] + path.split('.')) for path in paths]
>>> results
[55, 75, 'c']
However, you might have lists in your data, so this approach won't work as long as you don't get rid of them. Therefore you could use a function slightly more complicated than dict.get
:
from functools import reduce
results = [reduce(lambda o, k: o[int(k)] if isinstance(o, list) else o[k],
[my_dict] + path.split('.')) for path in paths]
Now everything works with your original data!
>>> paths = ['m1.m2.m3','m1.m2.m4','ml2.2']
>>> my_dict= {
... 'm1': {
... 'm2': {
... 'm3': 55,
... 'm4' : 75
... }
... },
... 'ml2': ['a', 'b', 'c']
... }
>>> results = [reduce(lambda o, k: o[int(k)] if isinstance(o, list) else o[k],
... [my_dict] + path.split('.')) for path in paths]
>>> results
[55, 75, 'c']
Upvotes: 0