Reputation: 362
I have a nested dict, which can contain placeholders in both keys and values.
example_dict = {'dict1': {'%(map3)s': {'data': 'tmp'},
'%(map2)s': {'freshdata': 'testtest'}},
'dict2': {'%(map3)s': {'data': '%(map1)s'}, '%(map3)s': {'status': 'available'}}}
I have a mapping dict, with the placeholder mapping:
mapping_dict= {
"map1": [1,2,2],
"map2": "qwerz",
"map3": "asdfasdf"
}
If the placeholder is at a VALUE position, it can also happen that the corresponding mapping of the mapping_dict contains another datatype than a string as value, e.g. list or an int. How can I pass this datatype to the original dict? I don't know howto make a placeholder e.g. for a list.
Info: It can happen that the mapping_dict contains more keys than the given example_dict contains.
I would like to have a function which replaces the placeholders of a given dict with the values of the mapping_dict.
What would be a good recursive implementation for this?
Upvotes: 1
Views: 2185
Reputation: 142156
Using a stack to navigate the key value pairs, you can pop out keys to rename them and for the values, do the same except try and see if you can evaluate them as Python literals using ast.literal_eval
to handle your list case.
import ast
from copy import deepcopy
example_dict = {
'dict1': {
'%(map3)s': {'data': 'tmp'},
'%(map2)s': {'freshdata': 'testtest'}
},
'dict2': {
'%(map3)s': {'data': '%(map1)s'}
}
}
mapping_dict= {
"map1": [1,2,2],
"map2": "qwerz",
"map3": "asdfasdf"
}
def sub_placeholders(orig, subs):
d = deepcopy(orig)
todo = [d]
while todo:
nxt = todo.pop()
for k, v in nxt.items():
nxt[k % mapping_dict] = nxt.pop(k)
if isinstance(v, dict):
todo.append(v)
elif isinstance(v, str):
nxt[k] = v % subs
try:
nxt[k] = ast.literal_eval(nxt[k])
except ValueError:
pass
return d
Running sub_placeholders(example_dict, example_mapping)
will give you:
{'dict1': {'asdfasdf': {'data': 'tmp'}, 'qwerz': {'freshdata': 'testtest'}},
'dict2': {'asdfasdf': {'data': [1, 2, 2]}}}
Upvotes: 3
Reputation: 123473
I think this does what you want, recursively. It creates a copy of the original dictionary and then modifies that so original can be reused.
from pprint import pprint
import copy
try:
stringtype = basestring
except NameError:
stringtype = str # Python 3
def subst(mapping, replacements):
def do_subst(mapping, replacements):
for k, v in list(mapping.items()):
newk, newv = k, v
changed = False
if isinstance(k, stringtype):
newk = k % replacements
if newk != k:
changed = True
if isinstance(v, stringtype):
newv = v % replacements
if newv != v:
changed = True
elif isinstance(v, dict):
newv = do_subst(v, replacements)
if newv != v:
changed = True
if changed:
del mapping[k]
mapping[newk] = newv
return mapping
return do_subst(copy.deepcopy(mapping), replacements)
example_dict = {'dict1': {'%(map1)s': {'data': 'tmp'},
'%(map2)s': {'freshdata': 'testtest'}},
'dict2': {'%(map1)s': {'data': '%(map1)s'},
'%(map2)s': {'status': 'available'}}}
mapping_dict= {"map1": "asdf", "map2": "qwerz"}
print('Before')
pprint(example_dict)
result = subst(example_dict, mapping_dict)
print('After')
pprint(result)
Output:
Before
{'dict1': {'%(map1)s': {'data': 'tmp'}, '%(map2)s': {'freshdata': 'testtest'}},
'dict2': {'%(map1)s': {'data': '%(map1)s'},
'%(map2)s': {'status': 'available'}}}
After
{'dict1': {'asdf': {'data': 'tmp'}, 'qwerz': {'freshdata': 'testtest'}},
'dict2': {'asdf': {'data': 'asdf'}, 'qwerz': {'status': 'available'}}}
Upvotes: 1
Reputation: 473873
Here is one recursive option deleting the existing keys and adding new keys them applying the "format" with "named" placeholders. Note: we are modifying the input dictionary in this case:
from pprint import pprint
example_dict = {'dict1': {'%(map1)s': {'data': 'tmp'}, '%(map2)s': {'freshdata': 'testtest'}},
'dict2': {'%(map1)s': {'data': '%(map1)s'}, '%(map2)s': {'status': 'available'}}}
mapping_dict= {
"map1": "asdf",
"map2": "qwerz",
}
def apply_placeholder(d, placeholder):
for key, value in d.items():
del d[key]
if isinstance(value, dict):
d[key % placeholder] = value
apply_placeholder(value, placeholder)
else:
d[key % placeholder] = value % placeholder
apply_placeholder(example_dict, mapping_dict)
pprint(example_dict)
Prints:
{'dict1': {'asdf': {'data': 'tmp'}, 'qwerz': {'freshdata': 'testtest'}},
'dict2': {'asdf': {'data': 'asdf'}, 'qwerz': {'status': 'available'}}}
I don't particularly like the del
calls and modifying the input object here and would be happy to see a better option.
Upvotes: 3