Reputation: 29
this or similar to was maybe asked in the past but I couldn't find a solution to suit my case. I currently have this following dict:
my_dict = {
"test": [
{
"inner_test": [
{
"name": "Jack"
},
{
"name": "Ann"
}
]
},
{
"inner_test": [
{
"name": "David"
},
{
"name": "Danny"
}
],
"some_test": [
3,
[
{
"name": "Benny"
},
{
"name": "Ann"
}
]
]
}
]
}
I am using custom python libraries to get a path of a specific key (I don't know the nesting depth nor the place of the key), for example I want to get the path to the 1st 'Ann' value so the variable will contain
test_var = ['test',0,'inner_test',1,'name']
Next step, I want to add another key+value to the code as a sibling to the path I found earlier, for example -
test_var_2 = test_var[:-1]
So now test_var_2 = ['test',0,'inner_test',1]
My question is, how can I send test_var_2
as a path variable instead of sending
`my_dict['test',0,'inner_test',1]`
For example if I want to add another key,value pair like -
my_dict['test',0,'inner_test',1, 'country'] = 'a_country'
but send it like this:
my_dict[test_var_2] = 'a_country'
I tried converting test_var_2
to a tuple but I got a 'Key Error'
In addition, I tried using this library - https://pypi.org/project/dict-deep/
to no avail as well.
Thanks :)
Upvotes: 0
Views: 537
Reputation: 23753
how can I send test_var_2 as a path variable
Write a function to iteratively work through the path.
# - this one is recursive
def f(vars,d):
if not vars:
return d
d = d[vars[0]]
return f(vars[1:],d)
# - "normal" iteration
def g(vars,d):
vars = iter(vars)
val = d[next(vars)]
for item in vars:
val = val[item]
return val
>>>f(test_var_2,my_dict)
{'name': 'Ann'}
You could then make a partial function specific to the dictionary.
import functools
mydict_pathfinder = functools.partial(f,d=my_dict)
#mydict_pathfinder(test_var_2)
Or specific to the path through any similarly structured dictionary:
q = functools.partial(f,test_var_2)
#q(my_dict)
You could use operator.itemgetter to make a callable for each path segment then compose those callables. Producing a callable specific to the path.
import operator
path = [operator.itemgetter(item) for item in test_var_2]
def compose(funcs):
def wrapped(d):
func = funcs[0](d)
for f in funcs[1:]:
func = f(func)
return func
return wrapped
pathfinder = compose(path)
#pathfinder(my_dict)
add another key,value
Get the dict and update it.
>>> target = g(test_var_2,my_dict)
>>> target.update({'foo':'bar'})
>>> from pprint import pprint
>>> pprint(my_dict)
{'test': [{'inner_test': [{'name': 'Jack'}, {'foo': 'bar', 'name': 'Ann'}]},
{'inner_test': [{'name': 'David'}, {'name': 'Danny'}],
'some_test': [3, [{'name': 'Benny'}, {'name': 'Ann'}]]}]}
>>> g(test_var_2,my_dict).update({'another':'one'})
>>> pprint(my_dict)
{'test': [{'inner_test': [{'name': 'Jack'},
{'another': 'one', 'foo': 'bar', 'name': 'Ann'}]},
{'inner_test': [{'name': 'David'}, {'name': 'Danny'}],
'some_test': [3, [{'name': 'Benny'}, {'name': 'Ann'}]]}]}
>>> pathfinder(my_dict).update({999:000})
>>> pathfinder(my_dict).update({'x':'y'})
>>>pprint(my_dict)
{'test': [{'inner_test': [{'name': 'Jack'}, {999: 0, 'name': 'Ann', 'x': 'y'}]},
{'inner_test': [{'name': 'David'}, {'name': 'Danny'}],
'some_test': [3, [{'name': 'Benny'}, {'name': 'Ann'}]]}]}
If you want to use subscription - my_dict[test_var_2].update({'new':'item'})
- you will have to write a class with a __getitem__
method that iteratively walks the dictionary using a process similar to those shown above. Something like.
class Pathfinder:
def __init__(self,d):
self.d = d
def __getitem__(self,item):
# use any of the functions above
return f(item)
pf = Pathfinder(my_dict)
>>> pf[test_var_2].update({'did it':'work?'})
>>> pprint(my_dict)
{'test': [{'inner_test': [{'name': 'Jack'},
{'did it': 'work?', 'name': 'Ann'}]},
{'inner_test': [{'name': 'David'}, {'name': 'Danny'}],
'some_test': [3, [{'name': 'Benny'}, {'name': 'Ann'}]]}]}
Upvotes: 1