Reputation: 111
Given a list called outlines containing tuples: (level, title), create a nested dictionary with depth based on the level and key value based on the title.
Example list:
[(1, Abstract)
(2, Background)
(2, Methods)
(2, Results)
(3, Statistics)
(3, Images)
(1, Introduction)]
This should output:
{
"Abstract": {
"Background": {},
"Methods": {},
"Results": {
"Statistics": {},
"Images": {}
}
},
"Introduction": {}
}
So far I've tried a recursive solution but so far has resulted in untraceable erroneous behavior. This is the best solution I've come up with so far, but I have trouble preventing duplicates in different levels due to the predefined for-loop:
def structure(outlines, current_level=1, previous_title=''):
section = dict()
for i, (level, title) in enumerate(outlines):
if level == current_level:
section[title] = {}
previous_title = title
elif level > current_level:
section[previous_title] = structure(outlines[i:], level)
elif level < current_level:
pass # Unknown
return section
Any tips?
Upvotes: 0
Views: 367
Reputation: 1797
We already have a working solution thanks to John R. Paul, but I wanted a solution which doesn't rely on dictionaries being ordered. As an added bonus, the solution is non-recursive (although John R. Paul's solution is tail-recursive and thus can be trivially rewritten to use a while
loop).
def add_all(generator_of_pairs):
stack = [{}]
for (level, name) in generator_of_pairs:
del stack[level:]
new_dict = {}
stack[level - 1][name] = new_dict
stack.append(new_dict)
return stack[0]
Note that this solution assumes that nesting levels cannot increase by more than 1 from 1 element to the next - but hopefully it should be obvious that this assumption is necessary.
Upvotes: 1
Reputation: 752
This is my recursive solution:
import json
inlist = [
(1, "Abstract"),
(2, "Background"),
(2, "Methods"),
(2, "Results"),
(3, "Statistics"),
(3, "Images"),
(1, "Introduction"),
]
out = dict()
# IMPORTANT: Dictionaries are ordered in Python 3.6 (under the CPython implementation at least) unlike in previous incarnations.
def addElem(dest_level, new_elem, current_container, current_level=1):
# list(current_container.keys())[-1] gets the most recently added element at the current level.
if (current_level < dest_level):
addElem(dest_level, new_elem, current_container[list(current_container.keys())[-1]], current_level+1)
else:
current_container[new_elem] = dict()
for el in inlist:
addElem(el[0], el[1], out)
print(json.dumps(out, indent=2))
Output:
{
"Abstract": {
"Background": {},
"Methods": {},
"Results": {
"Statistics": {},
"Images": {}
}
},
"Introduction": {}
}
Upvotes: 0
Reputation: 1294
Assuming that the order matters then this would work:
generator = [
(1, 'Abstract'),
(2, 'Background'),
(2, 'Methods'),
(2, 'Results'),
(3, 'Statistics'),
(3, 'Images'),
(1, 'Introduction')
]
# keep track of latest place in the dict
current_tree = []
# dict that you want to generate
d = {}
for i, value in generator:
# if this, then you are at the highest level
# so just add your value at the top
if current_tree==[] or i==1:
d[value] = {}
current_tree = [value]
# otherwise go back in the tree and add your value
else:
tmp_d = d
for key in current_tree[:i-1]:
tmp_d = tmp_d[key]
tmp_d[value] = {}
current_tree = current_tree[:i] + [value]
This returns:
{'Abstract': {'Background': {'Images': {}, 'Statistics': {}},
'Methods': {},
'Results': {}},
'Introduction': {}}
Upvotes: 0