【问题标题】:Search nested json / dict for multiple key values matching specified keys在嵌套的 json / dict 中搜索与指定键匹配的多个键值
【发布时间】:2019-03-04 16:36:49
【问题描述】:

我有一个 Python 对象,它具有多层字典和列表,其中包含我需要从中获取值的键。我使用递归生成器找到了一个answer,它允许我提取一个键的值,但不能提取多个键的值。代码如下:

with open('data.json') as f:
    json_data = json.load(f)

def find_key(obj, key):
    if isinstance(obj, dict):
        yield from iter_dict(obj, key, [])
    elif isinstance(obj, list):
        yield from iter_list(obj, key, [])

def iter_dict(d, key, indices):
    for k, v in d.items():
        if k == key:
            yield indices + [k], v
        if isinstance(v, dict):
            yield from iter_dict(v, key, indices + [k])
        elif isinstance(v, list):
            yield from iter_list(v, key, indices + [k])

def iter_list(seq, key, indices):
    for k, v in enumerate(seq):
        if isinstance(v, dict):
            yield from iter_dict(v, key, indices + [k])
        elif isinstance(v, list):
            yield from iter_list(v, key, indices + [k])


for c in find_key(json_data, 'customer_count'):
    print(c)

结果:

(['calendar', 'weeks', 0, 'days', 1, 'availabilities', 0, 'customer_count'], 14)
(['calendar', 'weeks', 0, 'days', 2, 'availabilities', 0, 'customer_count'], 7)

另一个post 有一个提取多个键的示例,但不会递归整个对象:

[...]
keys = ("customer_count", "utc_start_at", "non_resource_bookable_capacity")
for k in keys:
    keypath, val = next(find_key(json_data, k))
    print("{!r}: {!r}".format(k, val))

结果:

'customer_count': 14
'utc_start_at': '2018-09-29T16:45:00+0000'
'non_resource_bookable_capacity': 18

如何遍历整个对象并提取上面显示的三个键?

我想要的结果应该是这样的:

'customer_count': 14
'utc_start_at': '2018-09-29T16:45:00+0000'
'non_resource_bookable_capacity': 18

'customer_count': 7
'utc_start_at': '2018-09-29T16:45:00+0000'
'non_resource_bookable_capacity': 25

sample json

【问题讨论】:

标签: python json dictionary nested


【解决方案1】:

下面的示例函数在字典(包括所有嵌套字典)中搜索与您要查找的键列表匹配的键/值对。此函数递归循环遍历字典和任何嵌套字典并列出它包含的列表,以构建所有可能的字典列表以检查匹配键。

def find_key_value_pairs(q, keys, dicts=None):
    if not dicts:
        dicts = [q]
        q = [q]  

    data = q.pop(0)
    if isinstance(data, dict):
        data = data.values()

    for d in data:
        dtype = type(d)
        if dtype is dict or dtype is list:
            q.append(d)
            if dtype is dict:
                dicts.append(d)

    if q:
        return find_key_value_pairs(q, keys, dicts)

    return [(k, v) for d in dicts for k, v in d.items() if k in keys]

下面的示例使用json.loads 将类似于您的 json 的示例数据集转换为 dict,然后再将其传递给函数。

import json

json_data = """
{"results_count": 2, "results": [{"utc_start_at": "2018-09-29T16:45:00+0000", "counts": {"customer_count": "14", "other_count": "41"}, "capacity": {"non-resource": {"non_resource_bookable_capacity": "18", "other_non_resource_capacity": "1"}, "resource_capacity": "10"}}, {"utc_start_at": "2018-10-29T15:15:00+0000", "counts": {"customer_count": "7", "other_count": "41"}, "capacity": {"non-resource": {"non_resource_bookable_capacity": "25", "other_non_resource_capacity": "1"}, "resource_capacity": "10"}}]}
"""
data = json.loads(json_data) # json_data is a placeholder for your json
keys = ['results_count', 'customer_count', 'utc_start_at', 'non_resource_bookable_capacity']
results = find_key_value_pairs(data, keys)
for k, v in results:
    print(f'{k}: {v}')
# results_count: 2
# utc_start_at: 2018-09-29T16:45:00+0000
# utc_start_at: 2018-10-29T15:15:00+0000
# customer_count: 14
# customer_count: 7
# non_resource_bookable_capacity: 18
# non_resource_bookable_capacity: 25

【讨论】:

  • 我最近注意到该函数正在随机化新列表中元素的顺序(结果)。你能想到会发生这种情况的任何原因,或者如何确保元素的顺序一致吗?更新script
  • @TomAudre - 这可能是因为原始函数通过向后传递给search_queue 的列表工作(只是因为我没有花太多心思来保持这个函数的顺序)。也就是说,我编辑了答案以通过列表队列转发工作,我认为这应该可以让你得到你正在寻找的顺序(但我不是 100% 确定)。
  • 感谢您的更新。我觉得我的环境有些奇怪。在 OS X(python 3.7)上,我在元素中得到一致的顺序。在我的 Debian 8 服务器(python 3.4.2)上,我仍然得到随机顺序。再次感谢您再次查看。
  • @TomAudre 那是因为在 python 3.6 之前,字典是无序的。
猜你喜欢
  • 1970-01-01
  • 2014-04-01
  • 2018-09-10
  • 1970-01-01
  • 1970-01-01
  • 2020-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多