12

当值可能在嵌套字典中多次存在时,我正在努力处理嵌套字典,并为特定值返回嵌套的父键。例如:

example_dict = { 'key1' : 'value1',
                 'key2' : 'value2',
                 'key3' : { 'key3a': 'value3a' },
                 'key4' : { 'key4a': { 'key4aa': 'value4aa',
                                       'key4ab': 'value4ab',
                                       'key4ac': 'value1'},
                            'key4b': 'value4b'}
                }

您会注意到 'value1' 在上面的字典中出现了两次,我想创建一个函数来返回单个列表或一系列列表,以标识不同的父键,在本例中为 'key1 ' 和 ('key4', 'key4a', key4ac)。

此类问题已在本网站的其他地方处理过,当时正在寻找的值只出现一次,并且很容易由以下递归函数处理:

def find_key(d,key):
    for k,v in d.items():
        if isinstance(v,dict):
            p = find_key(v,key)
            if p:
                return [k] + p
        elif v == key:
            return [k]

print find_key(example_dict,'value4ac').

如果你在字典上运行上面的代码,我只能得到一个父键的答案。任何帮助将不胜感激,谢谢!

4

2 回答 2

12

除非你只是做一个单一的搜索(或者你在内存上非常受限但有 CPU 时间来燃烧......),你会想要构建一个反向查找字典,然后你就可以使用它。


为了使这更容易,我将分两步进行。首先,将嵌套字典转换为键路径字典:

def keypaths(nested):
    for key, value in nested.iteritems():
        if isinstance(value, collections.Mapping):
            for subkey, subvalue in keypaths(value):
                yield [key] + subkey, subvalue
        else:
            yield [key], value

list(keypaths(example_dict))如果它的作用不明显,请打印出来。


现在,你如何创建一个反向字典?对于一对一的映射,您可以这样做:

reverse_dict = {value: keypath for keypath, value in keypaths(example_dict)}

但是对于像你这样的多对一映射,相反的是一对多,所以我们需要将每个值映射到一个键列表。所以:

reverse_dict = {}
for keypath, value in keypaths(example_dict):
    reverse_dict.setdefault(value, []).append(keypath)

现在你不需要任何花哨的东西;只需对以下内容进行正常的 dict 查找reverse_dict

>>> reverse_dict['value2']
[('key2',)]
>>> reverse_dict['value1']
[('key1',), ('key4', 'key4a', 'key4ac')]
>>> reverse_dict['value3']
KeyError: 'value3'

如果您希望最后一个返回[]而不是提高 a KeyError,则可以使用 adefaultdict(list)而不是 plain dict,然后就不需要setdefault.


无论如何,构建这种反向映射所花费的时间只比通过蛮力进行单次搜索所花费的时间长一点,所以如果你进行 100 次搜索,这种方式会快近 100 倍,因为也更简单。

于 2013-09-16T04:29:09.990 回答
7

这是一种解决方案:

from copy import copy

example_dict = { 'key1' : 'value1',
                 'key2' : 'value2',
                 'key3' : { 'key3a': 'value3a' },
                 'key4' : { 'key4a': { 'key4aa': 'value4aa',
                                       'key4ab': 'value4ab',
                                       'key4ac': 'value1'},
                            'key4b': 'value4b'}
                }


result = []
path = []

def get_keys(d, target):
    for k, v in d.iteritems():
        path.append(k)
        if isinstance(v, dict):
            get_keys(v, target)
        if v == target:
            result.append(copy(path))
        path.pop()

结果:

>>> get_keys(example_dict, 'value1')
>>> result
[['key1'], ['key4', 'key4a', 'key4ac']]
于 2013-09-16T01:44:21.127 回答