【问题标题】:Creating nested dictionary from list of tuples从元组列表创建嵌套字典
【发布时间】:2021-08-14 18:52:04
【问题描述】:

给定一个包含元组的称为轮廓的列表:(级别,标题),创建一个嵌套字典,其深度基于级别,键值基于标题。

示例列表:
[(1, 摘要)
(2、背景)
(2、方法)
(2、结果)
(3、统计)
(3, 图片)
(一、简介)]

这应该输出:

{
"Abstract": {
    "Background": {}, 
    "Methods": {}, 
    "Results": {
        "Statistics": {},
        "Images": {}
    }
},
"Introduction": {}
}

到目前为止,我已经尝试了一种递归解决方案,但到目前为止已经导致无法追踪的错误行为。这是迄今为止我提出的最佳解决方案,但由于预定义的 for 循环,我无法防止不同级别的重复:

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

有什么建议吗?

【问题讨论】:

  • 如果两个可能的父节点具有相同的深度,由什么决定应该选择哪一个?
  • @JohnPaulR 存在顺序:如果等级增加,就会成为上一个节点的子节点。连续的同级节点将成为兄弟节点。
  • 好的,我根据这个信息更新了答案。

标签: python json dictionary recursion formatting


【解决方案1】:

假设顺序很重要,那么这将起作用:

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]

这会返回:

{'Abstract': {'Background': {'Images': {}, 'Statistics': {}},
              'Methods': {},
              'Results': {}},
 'Introduction': {}}

【讨论】:

    【解决方案2】:

    这是我的递归解决方案:

    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))
    

    输出:

    {
      "Abstract": {
        "Background": {},
        "Methods": {},
        "Results": {
          "Statistics": {},
          "Images": {}
        }
      },
      "Introduction": {}
    }
    

    【讨论】:

    • 在阅读您的评论后,才意识到这并不能回答您的问题。正在修改中。
    • 如果我理解正确的话,现在应该可以正确解决您的问题。有什么问题吗?
    • 我使用您的代码收到了所需的输出。非常感谢!
    【解决方案3】:

    感谢 John R. Paul,我们已经有了一个可行的解决方案,但我想要一个不依赖于订购字典的解决方案。作为额外的好处,该解决方案是非递归的(尽管 John R. Paul 的解决方案是尾递归的,因此可以简单地重写为使用 while 循环)。

    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]
    

    请注意,此解决方案假定嵌套级别从 1 个元素到下一个元素的增加不能超过 1 - 但希望很明显,此假设是必要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-04
      • 2021-11-20
      • 2021-04-19
      • 2018-01-16
      • 1970-01-01
      • 2020-10-22
      相关资源
      最近更新 更多