【问题标题】:How to turn a list into nested dict in Python如何在 Python 中将列表转换为嵌套字典
【发布时间】:2011-12-01 00:13:30
【问题描述】:

需要转x:

X = [['A', 'B', 'C'], ['A', 'B', 'D']]

进入Y:

Y = {'A': {'B': {'C','D'}}}

更具体地说,我需要从绝对路径列表中创建一个文件夹和文件树,如下所示:

paths = ['xyz/123/file.txt', 'abc/456/otherfile.txt']

其中,每个路径都是split("/"),如伪示例中的['A', 'B', 'C']。

由于这代表文件和文件夹,显然,在同一级别(数组的索引)上,相同的名称字符串不能重复。

【问题讨论】:

  • 这是什么? {'C','D'}
  • 你想用嵌套字典解决什么问题?
  • 如果文件夹 A 包含目录 B 和文件 X 怎么办?应该如何表示?
  • 这可以接受吗? {'A': {'B': {'C': {}, 'D': {}}}}
  • 更一致,Y = {'A': {'B': {'C':{},'D':{}}}}

标签: python list dictionary nested


【解决方案1】:

您的问题陈述中存在逻辑上的不一致。如果你真的想要 ['xyz/123/file.txt', 'abc/456/otherfile.txt']

改为 {'xyz': {'123': 'file.txt}, 'abc': {'456': 'otherfile.txt'}}

然后您必须回答如何将没有前导文件夹的路径“abc.txt”插入到此数据结构中。顶级字典键是空字符串''吗?

【讨论】:

  • 如果顶层有一个名为 abc.txt 的文件,它将与 'xyz' 和 'abc' 对等,如 {'xyz':{...}, ' abc':{...}, 'abc.txt:{}} 。 {} 为空,表示它是一片叶子
  • “看起来”像文件或文件夹并不重要。最后一个是文件,其余的是文件夹。这不是真正的文件系统,顺便说一句。
【解决方案2】:

假设{'C', 'D'} 表示set(['C', 'D']) 并且您的Python 版本支持dict comprehension 和set comprehension,这是一个丑陋但有效的解决方案:

>>> tr = [[1, 2, 3], [1, 2, 4], [5, 6, 7]]
>>> {a[0]: {b[1]: {c[2] for c in [y for y in tr if y[1] == b[1]]} for b in [x for x in tr if x[0] == a[0]]} for a in tr}
{1: {2: set([3, 4])}, 5: {6: set([7])}}

至于你的例子:

>>> X = [['A', 'B', 'C'], ['A', 'B', 'D']]
>>> {a[0]: {b[1]: {c[2] for c in [y for y in X if y[1] == b[1]]} for b in [x for x in X if x[0] == a[0]]} for a in X}
{'A': {'B': set(['C', 'D'])}}

但请不要在实际应用中使用它:)

更新:这是一种适用于任意深度的方法:

>>> def todict(lst, d=0):
...     print lst, d
...     if d > len(lst):
...         return {}
...     return {a[d]: todict([x for x in X if x[d] == a[d]], d+1) for a in lst}
...
>>> todict(X)
{'A': {'B': {'C': {}, 'D': {}}}}

【讨论】:

  • 也不适用于任意深度,这可能是一个要求。
  • @agf true - 我不认为这是一个要求,但它可能是。我应该想出一个看起来更可怕的单线吗? :)
  • 哈,那会很吓人的。我认为马特有这个想法——它必须递归地完成。
【解决方案3】:

这应该非常接近您的需要:

def path_to_dict(path):
    parts = path.split('/')

    def pack(parts):
        if len(parts) == 1:
            return parts
        elif len(parts):
            return {parts[0]: pack(parts[1:])}
        return parts

    return pack(parts)

if __name__ == '__main__':
    paths = ['xyz/123/file.txt', 'abc/456/otherfile.txt']
    for path in paths:
        print '%s -> %s' % (path, path_to_dict(path))

结果:

xyz/123/file.txt -> {'xyz': {'123': ['file.txt']}}
abc/456/otherfile.txt -> {'abc': {'456': ['otherfile.txt']}}

【讨论】:

  • 如何加入所有字典,例如如果您有其他路径(如 xyz/123/b.a 和 abc/sss/ooo/1.a)与这些路径重叠?
  • 如果'xyz'包含目录和文件怎么办?
  • 马特,我正要问和 agf 一样的问题。到目前为止,我得到了自己。我实际上需要一个 dict 合并。会有重叠,所以我不能只是批量替换节点,需要在每个级别实际合并。
  • 以上链接无效重定向到垃圾邮件
【解决方案4】:
X = [['A', 'B', 'C'], ['A', 'B', 'D'],['W','X'],['W','Y','Z']]
d = {}

for path in X:
    current_level = d
    for part in path:
        if part not in current_level:
            current_level[part] = {}
        current_level = current_level[part]

这给我们留下了包含{'A': {'B': {'C': {}, 'D': {}}}, 'W': {'Y': {'Z': {}}, 'X': {}}}的d。任何包含空字典的项目要么是文件,要么是空目录。

【讨论】:

  • current_level 是从哪里添加到 d 中的?
  • @Tyler Hilbert current_level 是 d!表达式current_level = d 不会复制d,它只是创建一个名为current_level 的对d 的新引用;)如果current_level 是这样创建的副本,上面的代码将不起作用:current_level = d[:] 或像这样:current_level = copy.deepcopy(d),但因为它不是 - 它只是工作!
  • @tsveti_iko 如果是参考,为什么current_level = current_level[part]这一行不只修改current_level而不修改d?我已经检查了调试器,在这个阶段之后(例如,当我们在第一个元素A 时),我们将得到:current_level = {} 而d = {'A': {}}。谢谢!
【解决方案5】:

我在 twitter 上被问到这个问题,并使用函数式编程提出了这个巧妙的解决方案,我想我不妨在这里分享一下。

from functools import reduce
X = [['A', 'B', 'C'], ['A', 'B', 'D']]
Y = [reduce(lambda x, y: {y:x}, Y[::-1]) for Y in X]

返回:

[{'A': {'B': 'C'}}, {'A': {'B': 'D'}}]

根据需要。

对于一个更简单的问题,您希望将一个列表表示为具有嵌套键的字典,这就足够了:

from functools import reduce
X = ['A', 'B', 'C']
reduce(lambda x, y: {y:x}, X[::-1])

返回:

{'A': {'B': 'C'}}

【讨论】:

    猜你喜欢
    • 2023-03-11
    • 1970-01-01
    • 2020-05-16
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 2022-12-05
    • 2020-09-03
    相关资源
    最近更新 更多