'How to 'deep-merge' dicts?

Imagine the following dicts:

a = {'key1': {'subkey1': [1, 2, 3]}}
b = {'key1': {'subkey2': [1, 2, 3]}}

I'd like to merge them to get

c = {'key1': {'subkey1': [1, 2, 3],
              'subkey2': [1, 2, 3]}}

Extra nice would be a solution that returns deep-copies from a and b which I can alter without altering a or b.

c = {**a, **b}

looks nice but seems to be the same as c = copy(a).update(b) which returns same as b in my case because key1 gets overwritten by the update.

You can of course do this by hand like this (found in another answer):

def combine_dict(map1: dict, map2: dict):
    def update(d: dict, u: dict):
        for k, v in u.items():
            if isinstance(v, collections.Mapping):
                r = update(d.get(k, {}), v)
                d[k] = r
            else:
                d[k] = u[k]
        return d
    _result = {}
    update(_result, map1)
    update(_result, map2)
    return _result

But we have Python 3.5 now - maybe things have changed?



Solution 1:[1]

You need recursion to accomplish this. Luckily milanboers on GitHub saved us from hours of work and possible brain damage.

def deep_merge(dict1: dict, dict2: dict) -> dict:
    """ Merges two dicts. If keys are conflicting, dict2 is preferred. """
    def _val(v1, v2):
        if isinstance(v1, dict) and isinstance(v2, dict):
            return deep_merge(v1, v2)
        return v2 or v1
    return {k: _val(dict1.get(k), dict2.get(k)) for k in dict1.keys() | dict2.keys()}


a = {'key1': {'subkey1': [1, 2, 3]}}
b = {'key1': {'subkey2': [1, 2, 3]}}

a = deep_merge(a, b)
print(a)

Results in:

{'key1': {'subkey2': [1, 2, 3], 'subkey1': [1, 2, 3]}}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ss1