问题 Python - 在嵌套字典中查找特定值的父键


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

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

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

这个类型的问题在本网站的其他地方得到了处理,当Value一个只查找时出现一次,并且很容易通过以下递归函数处理:

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

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


4439
2017-09-16 01:12


起源

您是在反复进行此类搜索,还是仅进行过一次?如果你做的不止一个,你几乎肯定想要创建一个反向映射字典,只需访问它,而不是每次都强力搜索整个字典。 - abarnert


答案:


除非你只是进行一次搜索(或者你在内存上受到极大的限制,但有时间需要刻录...),否则你需要构建一个反向查找字典,然后就可以使用它了。


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

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)

而现在你不需要任何花哨的东西;只是做一个正常的字典查找 reverse_dict

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

如果你更喜欢最后一个回来 [] 而不是提高 KeyError,你可以使用 defaultdict(list) 而不是平原 dict,然后你不需要 setdefault


无论如何,构建这种反向映射所花费的时间只比通过强力进行单次搜索所花费的时间稍长一些,所以如果你进行100次搜索,它的速度将快几百倍,就像更简单。


9
2017-09-16 04:29



这很棒。 Thnx花时间解释并把它放在一起。反向映射是有道理的。我的问题背后的动机是处理可能具有状态的json数据:在字典样式的返回响应中多次'错误',然后使用'错误'键路径来识别错误的数据馈送组件,以及它的性质错误(即'错误信息')。我想知道json数据是否通常以标准化格式出现,这样有标准的序列化器,或者在这种情况下,必须根据正在处理的json格式进行编码? - 干杯 - Mike
它简单易懂。谢谢@abarnet - Md. Nazmul Haque Sarker
import collections - bshea


这是一个解决方案:

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

6
2017-09-16 01:44



谢谢你把这个放在一起 - 迈克。 - Mike
@Mike为什么没有upvotes(除了我的)? - gsamaras