合并多个嵌套字典

时间:2020-10-16 16:22:15

标签: python python-3.x dictionary merge

我有不明数量的字典,其中包含一组未知的键,例如:

d1 = {'job': {'data': {'id': 'string'}}}
d2 = {'job': {'data': {'title': 'string'}}}
d3 = {'job': {'metadata': {'date': 'string'}}}
d4 = {'user': {'id': 'string'}}

我想要一个这样的组合字典:

{
    'job': {
        'data': {
            'id': 'string',
            'title': 'string'
        }
        'metadata': {
            'date': 'string'
        }
    },
    'user': {
        'id': 'string'
    }
}

内置的update不能满足我的需求:

>>> combined = {}
>>> combined.update(d1)
>>> combined.update(d2)
>>> combined.update(d3)
>>> combined.update(d4)
>>> combined
{'job': {'metadata': {'date': 'string'}}, 'user': {'id': 'string'}}

This question听起来确实很接近我想要的声音,但结果却相同:

>>> {**d1, **d2, **d3, **d4}
{'job': {'metadata': {'date': 'string'}}, 'user': {'id': 'string'}}

这个问题专门要求建立工会,但对我来说,这看起来并不像工会。我想念什么?

2 个答案:

答案 0 :(得分:1)

这是一种通用尝试,它使用recursiongeneratorreduce来回答您的问题:

from functools import reduce


def union_nested_dicts(a, b):
    a_keys = list(a.keys()) if isinstance(a, dict) else []
    b_keys = list(b.keys()) if isinstance(b, dict) else []
    for k in set(a_keys + b_keys):
        if not isinstance(a.get(k, {}), dict) or not isinstance(b.get(k, {}), dict):
            if a:
                # Or: yield from a.items()
                yield k, a.get(k, {})
            if b:
                yield from b.items()
        else:
            tmp = dict(union_nested_dicts(a.get(k, {}), b.get(k, {}))) 
            yield k, tmp


a = {'job': {'data': {'id': 'string'}}}
b = {'job': {'data': {'title': 'string'}}}
d3 = {'job': {'metadata': {'date': 'string'}}}
d4 = {'user': {'id': 'string'}}
out = reduce(
    lambda left, right: dict(union_nested_dicts(left, right)),
    [a, b, d3, d4]
)
print(out)

输出:

{
    'job': {
         'data': {'id': 'string', 'title': 'string'},
         'metadata': {'date': 'string'}
     },
     'user': {'id': 'string'}
}

答案 1 :(得分:1)

您要在AFAIU中合并具有相似键的字典,并为不同键更新

这是使用python 3.9的新语法的简单尝试:

def nested_union(a, b):
    new_dict = a.copy()

    for key, value in a.items():
        if key not in b:
            new_dict |= b  # new_dict.update(b)
        elif not isinstance(value, dict):
            new_dict = a | b  # {**a, **b}
        else:
            new_dict[key] = nested_union(value, b[key])

    return new_dict

我建议您对提供的代码进行单元测试,因为我的测试基于您的示例。带有* args的扩展名将提高使用过程中的可读性。