【问题标题】:Searching for keys in a nested dictionary [duplicate]在嵌套字典中搜索键[重复]
【发布时间】:2015-10-15 23:59:36
【问题描述】:

我在 Python 中有一个 JSON 对象,表示为嵌套的字典列表。 (字典的一些值就是字典本身,等等。)

我希望能够在这个嵌套字典结构的所有分支上搜索一个键。
当我找到密钥时,我希望能够返回通向它的完整密钥路径。

例如:我正在寻找具有“特殊地址密钥”的“特殊代理”,但并非所有特殊代理都拥有它,而且那些确实在其 JSON 中的路径不一致的情况下拥有它。

所以我搜索密钥Special Address code。 结果应该返回:

/'People'/'SpecialAgents'/'007'/'Special Address code'/  

所以我可以通过这种方式获得它的信息:

json_obj['People']['SpecialAgents']['007']['Special Address code']

请注意,这类似于 this question,但我需要找到的每个密钥实例的完整路径。

【问题讨论】:

标签: python


【解决方案1】:

您需要递归搜索。

您可以定义一个函数在您的输入 json 中进行深度搜索:

def find_in_obj(obj, condition, path=None):

    if path is None:
        path = []    

    # In case this is a list
    if isinstance(obj, list):
        for index, value in enumerate(obj):
            new_path = list(path)
            new_path.append(index)
            for result in find_in_obj(value, condition, path=new_path):
                yield result 

    # In case this is a dictionary
    if isinstance(obj, dict):
        for key, value in obj.items():
            new_path = list(path)
            new_path.append(key)
            for result in find_in_obj(value, condition, path=new_path):
                yield result 

            if condition == key:
                new_path = list(path)
                new_path.append(key)
                yield new_path 

然后我们可以使用similar SO question 中的示例 JSON 来测试递归搜索:

In [15]: my_json = { "id" : "abcde",
   ....:   "key1" : "blah",
   ....:   "key2" : "blah blah",
   ....:   "nestedlist" : [ 
   ....:     { "id" : "qwerty",
   ....:       "nestednestedlist" : [ 
   ....:         { "id" : "xyz",
   ....:           "keyA" : "blah blah blah" },
   ....:         { "id" : "fghi",
   ....:           "keyZ" : "blah blah blah" }],
   ....:       "anothernestednestedlist" : [ 
   ....:         { "id" : "asdf",
   ....:           "keyQ" : "blah blah" },
   ....:         { "id" : "yuiop",
   ....:           "keyW" : "blah" }] } ] } 

让我们找到键“id”的每个实例,并返回让我们到达那里的完整路径:

In [16]: for item in find_in_obj(my_json, 'id'):
   ....:     print item
   ....:     
['nestedlist', 0, 'nestednestedlist', 0, 'id']
['nestedlist', 0, 'nestednestedlist', 1, 'id']
['nestedlist', 0, 'id']
['nestedlist', 0, 'anothernestednestedlist', 0, 'id']
['nestedlist', 0, 'anothernestednestedlist', 1, 'id']
['id']

【讨论】:

  • +1 这行得通。好的。我测试了它在来自this question 的对象中搜索id。任何关于它是如何工作的,以及你从哪里得到的? (这些代码都是你从头开始写的吗?)
  • @LondonRob 此函数迭代键并存储当前路径的状态。它基于收益率回报(简化了解决方案)。从头开始写,然后是 OP 描述
  • 介意我稍微编辑一下以显示更完整的示例吗?它比您目前的单一结果更好地展示了您的工作。
  • @LondonRob 随时改进此答案(以及本网站上的任何其他答案:P)
【解决方案2】:

你需要搜索一棵树。这是最简单的方法。

它可以被增强 - 例如,最好使用 None 作为默认 arg 值而不是某些对象。此外,这是深度优先搜索 - 您可能只想获得一个结果,这就是宽度优先搜索更好的时候(如果您不了解这两个术语,请阅读 wikipedia 上的这两个术语)。

import json

example_json = """{
 "someList" : [
  {
   "x": {
    "y": {
     "z": "Some value"
    }
   }
  }, 
  {
   "x": {
    "y": {
     "a": "Wrong key"
    }
   }
  }
 ]
}
"""

struct = json.loads(example_json)

def find_all_with_key(wanted_key, tree, path=tuple()):
    if isinstance(tree, list):
        for idx, el in enumerate(tree):
            yield from find_all_with_key(wanted_key, el, path+(idx,))
    elif isinstance(tree, dict):
        for k in tree:
            if k == wanted_key:
                yield path +(k, )
        # you can add order of width-search by sorting result of tree.items()
        for k, v in tree.items(): 
            yield from find_all_with_key(wanted_key, v, path+(k,))

def retrieve(tree, path):
    for p in path:
        tree = tree[p]
    return tree

result = list(find_all_with_key("z", struct))
expected = [ ("someList", 0, "x", "y", "z") ]

assert result == expected
assert retrieve(struct, result[0]) == "Some value"

【讨论】:

  • 这是 python3 唯一的语法,你也不需要调用 .keys 来遍历字典键
  • 告诉你,这可以使用一些增强功能:P 另一个丑陋的事情是使用 isinstance(),但这不是重点。另外,我认为“仅限python3”并不是一件坏事——我强烈支持迁移到新行而不是使用p2.7。
  • 好吧,直到 python 的仙女教母出现并点击她的手指以使所有编写的 python 代码与 python 3 兼容我猜 python2 代码仍然必须编写。我认为isinstance() 没有任何问题,这与键不同;)
  • 已修复。无论如何,唯一的区别是在 p2 中你应该使用 for x in X: yield x 而不是 yield from X。更多信息:stackoverflow.com/a/17581397/1219585 如果有人会看
猜你喜欢
  • 2011-12-02
  • 2016-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-27
  • 2020-10-01
  • 2016-08-16
相关资源
最近更新 更多