如何在Python中将列表转换为嵌套字典

16

需要将 x 转换:

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

转换为 Y:

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

更具体地说,我需要从一组绝对路径创建一个文件夹和文件的树形结构,它看起来像这样:

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

假设每个路径都是根据伪例子中的['A','B','C']进行split("/")的结果。

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


2
这是什么?{'C','D'} - FogleBird
你试图用嵌套字典解决什么问题? - Steven Rumbalski
如果文件夹A包含目录B和文件X,应该如何表示? - Steven Rumbalski
这个是否可以接受?{'A': {'B': {'C': {}, 'D': {}}}} - FogleBird
更加一致,Y = {'A': {'B': {'C':{},'D':{}}}} - Dmitry Grinberg
显示剩余2条评论
6个回答

33
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]

这意味着d包含{'A': {'B': {'C': {}, 'D': {}}}, 'W': {'Y': {'Z': {}}, 'X': {}}}。任何包含空字典的项都是文件或空目录。


当前级别添加到d中的位置在哪里? - Tyler Hilbert
@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': {}}。谢谢! - ThePhi

6
假设 {'C', 'D'} 表示 set(['C', 'D']),并且您的 Python 版本支持 dict comprehensionset 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
1
@agf 确实 - 我并没有认为那是一个要求,但可能确实是。我应该想出一个更难看的单行代码吗? :) - Attila O.
1
哈哈,那只会变得可怕。我认为马特已经有了这个想法——必须递归地完成。 - agf

1

这应该非常接近你所需要的:

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.aabc/sss/ooo/1.a - agf
如果'xyz'包含目录和文件怎么办? - Steven Rumbalski
Matt,我正想问和agf一样的问题。到目前为止,我已经做了这个。实际上,我需要合并一个字典。由于存在重叠,所以我不能只是整体替换节点,需要在每个级别上进行实际合并。 - Dmitry Grinberg
深度合并字典:http://appdelegateinc.com/blog/2011/01/12/merge-deeply-nested-dicts-in-python/ - Matt Williamson
以上链接已失效,重定向到垃圾邮件。 - SpectreVert

1

我在 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'}}

这并不像预期的那样合并字典。期望的输出是 Y = {'A': {'B': {'C','D'}}} - dongle man

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:{}}。其中空{}表示它是一个叶子节点。 - Dmitry Grinberg
不管某些东西“看起来”像文件或文件夹。最后一个是文件,其余都是文件夹。顺便说一下,这不是真正的文件系统。 - Dmitry Grinberg

0

首先将键和值分开

x = [['A', 'B', 'C'], ['A', 'B', 'D']]
keys = [tuple(asd[:-1]) for asd in x]
values = [asd[-1] for asd in x]

现在使用它们来填充一个NestedDict

from ndicts.ndicts import NestedDict

nd = NestedDict()
for key, value in zip(keys, values):
    nd[key] = value

>>> nd
NestedDict({'A': {'B': 'D'}})
>>> nd.to_dict()
{'A': {'B': 'D'}}

安装ndicts,请执行pip install ndicts


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接