将任意长度的字典项展平为 Python 中的路径列表

Posted

技术标签:

【中文标题】将任意长度的字典项展平为 Python 中的路径列表【英文标题】:Flatten Arbitrary Length of Dictionary Items Into List of Paths in Python 【发布时间】:2014-12-28 03:54:04 【问题描述】:

所以,我已经阅读了很多关于在 Python 中递归地扁平化字典的文章。没有(除了一个)接近我正在寻找的东西。首先,我要完成的一个简单示例:

混合条目的示例字典:(键和值总是混合类型)

'a': ['b': 'c': 'd', 'e': 'f', 'g': 'h',
              'i': 'j': 'k': ['l'], 'm': 'n',
              'o': 'p': 'q': ['r', 's' ], 't': 'u'
              
       ]

期望的输出:

'a/b/c/d',
 'a/b/e/f',
 'a/b/g/h',
 'a/b/i/j/k/l',
 'a/b/i/j/m/n',
 'a/b/o/p/q/r',
 'a/b/o/p/q/s',
 'a/b/o/p/t/u'

该函数应该(理论上)也适用于列表。

为了解释一下我在做什么,我正在尝试通过 Mac plist 进行搜索,而其他通过键或值进行搜索的尝试充其量是不稳定的。作为补偿,我想尝试不同的方法。将字典转换为“路径”列表,然后搜索路径。

我自己尝试过(并且部分成功),然后我找到了一个更好的解决方案,形式如下:

def flatten(structure, key="", path="", flattened=None):
    if flattened is None:
        flattened = 
    if type(structure) not in(dict, list):
        flattened[((path + "/") if path else "") + key] = structure
    elif isinstance(structure, list):
        for i, item in enumerate(structure):
            flatten(item, "", "/".join(filter(None,[path,key])), flattened)
    else:
        for new_key, value in structure.items():
            flatten(value, new_key, "/".join(filter(None,[path,key])), flattened)
    return flattened

这很有效,但有一些不良影响。首先,输出如下:

'a/b/c'     : 'd',
 'a/b/e'     : 'f',
 'a/b/g'     : 'h',
 'a/b/i/j/k/': 'l',
 'a/b/i/j/m' : 'n',
 'a/b/o/p/q/': 's',
 'a/b/o/p/t' : 'u'

这将返回一个键/值对字典。我宁愿有一个字符串路径列表。其次,更重要的是,您会注意到,脚本已经剥离了值是列表的值。仅附加列表的最后一项。

'a/b/o/p/q/': 's' # there should be another entry with 'r' as the value.

我花了相当多的时间摆弄输出并试图完全解决问题,但无济于事。可能只是我对 Python 的理解不够,但我想要的输出应该是可以的。

我尽量不问问题,除非我已经用完了所有选项,我就在这里。请不要标记为重复,因为其他问题并不完全符合我的要求。

感谢您的时间和帮助/指导。

【问题讨论】:

'q': [ 'r', 's', 'x'],'t': 'u' 应该产生什么?您的示例中生成'a/b/c/d/o/p/q/r', 'a/b/c/d/o/p/q/r/s' 的部分在我看来应该是.../q/r, .../q/s 谢谢马特。你是对的。 ('.../q/r', '.../q/s', '.../q/x', '.../t/u') 我不假思索地创建了所需的输出。我已经编辑了我的问题。 也许flatten nested Python dictionaries, compressing keys 不是答案,但也许会有所帮助。 感谢 Mauro 提供的链接。我去看看! 【参考方案1】:

这是我在 Python 3.3+ 中的做法:

def flatten(exp):
    def sub(exp, res):
        if type(exp) == dict:
            for k, v in exp.items():
                yield from sub(v, res+[k])
        elif type(exp) == list:
            for v in exp:
                yield from sub(v, res)
        else:
            yield "/".join(res+[exp])
    yield from sub(exp, [])

测试:

l='a': ['b': 'c': 'd', 'e': 'f', 'g': 'h',
              'i': 'j': 'k': ['l'], 'm': 'n',
              'o': 'p': 'q': ['r', 's' ], 't': 'u'
              
       ]


for i in sorted(flatten(l)):
    print(i)

产量

a/b/c/d
a/b/e/f
a/b/g/h
a/b/i/j/k/l
a/b/i/j/m/n
a/b/o/p/q/r
a/b/o/p/q/s
a/b/o/p/t/u

EDIT 翻译成 Python 2 很简单:

def flatten(exp):
    def sub(exp, res):
        if type(exp) == dict:
            for k, v in exp.items():
                for r in sub(v, res+[k]):
                    yield r
        elif type(exp) == list:
            for v in exp:
                for r in sub(v, res):
                    yield r
        else:
            yield "/".join(res+[exp])
    for r in sub(exp, []):
        yield r

然后

>>> for i in sorted(flatten(l)):
...     print i
...
a/b/c/d
a/b/e/f
a/b/g/h
a/b/i/j/k/l
a/b/i/j/m/n
a/b/o/p/q/r
a/b/o/p/q/s
a/b/o/p/t/u

【讨论】:

谢谢你。我目前使用的是 2.7,所以我不能使用该解决方案,但如果/当我升级时,我会记住这一点。 @kdougan 没问题。请在下一个问题中指定您的 Python 版本。 FWIW,我添加了对 Python 2.7 的翻译。 注明。也感谢您的翻译。【参考方案2】:

Python 2.7:

def flatten(structure):
    if isinstance(structure, basestring):
        return [structure]
    ret = []
    if isinstance(structure, list):
        for v in structure:
            ret.extend(flatten(v))
    elif isinstance(structure, dict):
        for k, v in structure.items():
            ret.extend(k + '/' + f for f in flatten(v))
    return ret

print sorted(flatten(structure))

输出:

['a/b/c/d', 'a/b/e/f', 'a/b/g/h', 'a/b/i/j/k/l', 'a/b/i/j/m/n', 'a/b/o/p/q/r', 'a/b/o/p/q/s', 'a/b/o/p/t/u']

或者,如果您不关心订单,您可以简单地print flatten(structure)

【讨论】:

谢谢无关人员!这完美地工作。奇怪的是它总是简单的解决方案。好吧,再加上我自己过于复杂的倾向。

以上是关于将任意长度的字典项展平为 Python 中的路径列表的主要内容,如果未能解决你的问题,请参考以下文章

python之hash 字典 集合

Dask 到展平字典列

如何将 Pandas DataFrame 中的字典列表展平为几列?

在给定“路径”的情况下修改嵌套字典中的元素

Python:使用索引展平嵌套列表

pandas json_normalize 所有列都有嵌套字典展平