A
Alia Khouri
I was kind of wondering what ways are out there to elegantly expand
'$name' identifiers in nested dictionary value. The problem arose when
I wanted to include that kind of functionality to dicts read from yaml
files such that:
def func(input):
# do something
return output
where:
input = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/
file',
'dir': {'path': '$src', 'fullname': '$firstname $lastname'}}
output = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/
file',
'dir': {'path': 'c:/tmp/file', 'fullname': 'John Smith'}}
Doing this substitution easy done when you have a flat dict, but when
they got nested, I had to resort to an undoubtedly ugly function with
two recursive passes and obvious deficiencies.
Is there a better way to do this?
Thanks for any help...
AK
<code follows>
# test_recurse.py
from string import Template
from pprint import pprint
def expand(dikt):
''''firstname': 'John'}
True
'''
subs = {}
for key, value in dikt.items():
if '$' in value:
subs[key] = Template(value).substitute(dikt)
dikt.update(subs)
return dikt
dikt = {'firstname': 'John', 'lastname': 'Smith',
'fullname': '$firstname $lastname'}
#~ print expand(dikt)
d1 = {'firstname': 'John', 'lastname': 'Smith',
'dir': {'fullname': '$firstname $lastname'}
}
d2 = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/file',
'dir': {'fullname': '$firstname $lastname', 'path':
'$src'}
}
def rexpand(dikt):
subs = {}
names = {}
# pass 1
def recurse(_, sourceDict):
for key, value in sourceDict.items():
if isinstance(value, dict):
recurse({}, value)
elif '$' in value:
subs[key] = value
else:
names[key] = value
recurse({}, dikt)
print 'subs', subs
print 'names', names
print 'dikt (before):', dikt
for key, value in subs.items():
subs[key] = Template(value).substitute(names)
# -----------------------------------------------------
# pass 2
output = {}
def substitute(targetDict, sourceDict):
for key, value in sourceDict.items():
if isinstance(value, dict):
new_target = targetDict.setdefault(key, {})
substitute(new_target, value)
else:
targetDict[key] =
Template(value).substitute(names)
substitute(output, dikt)
print 'output:', output
return output
rexpand(d2)
'$name' identifiers in nested dictionary value. The problem arose when
I wanted to include that kind of functionality to dicts read from yaml
files such that:
def func(input):
# do something
return output
where:
input = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/
file',
'dir': {'path': '$src', 'fullname': '$firstname $lastname'}}
output = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/
file',
'dir': {'path': 'c:/tmp/file', 'fullname': 'John Smith'}}
Doing this substitution easy done when you have a flat dict, but when
they got nested, I had to resort to an undoubtedly ugly function with
two recursive passes and obvious deficiencies.
Is there a better way to do this?
Thanks for any help...
AK
<code follows>
# test_recurse.py
from string import Template
from pprint import pprint
def expand(dikt):
''''firstname': 'John'}
True
'''
subs = {}
for key, value in dikt.items():
if '$' in value:
subs[key] = Template(value).substitute(dikt)
dikt.update(subs)
return dikt
dikt = {'firstname': 'John', 'lastname': 'Smith',
'fullname': '$firstname $lastname'}
#~ print expand(dikt)
d1 = {'firstname': 'John', 'lastname': 'Smith',
'dir': {'fullname': '$firstname $lastname'}
}
d2 = {'firstname': 'John', 'lastname': 'Smith', 'src': 'c:/tmp/file',
'dir': {'fullname': '$firstname $lastname', 'path':
'$src'}
}
def rexpand(dikt):
subs = {}
names = {}
# pass 1
def recurse(_, sourceDict):
for key, value in sourceDict.items():
if isinstance(value, dict):
recurse({}, value)
elif '$' in value:
subs[key] = value
else:
names[key] = value
recurse({}, dikt)
print 'subs', subs
print 'names', names
print 'dikt (before):', dikt
for key, value in subs.items():
subs[key] = Template(value).substitute(names)
# -----------------------------------------------------
# pass 2
output = {}
def substitute(targetDict, sourceDict):
for key, value in sourceDict.items():
if isinstance(value, dict):
new_target = targetDict.setdefault(key, {})
substitute(new_target, value)
else:
targetDict[key] =
Template(value).substitute(names)
substitute(output, dikt)
print 'output:', output
return output
rexpand(d2)