Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The best way to merge multi-nested dictionaries in Python 2.7

I have two nested dictionaries and I want to merge them into one (where second dict overrides first dict values). I saw a lot of beautiful solutions for merging "flat" (not nested) dictionaries, e.g.:

dict_result = dict1.copy()
dict_result.update(dict2)

or

dict_result = dict(dict1.items() + dict2.items())

or (my favorit one)

dict_result = dict(d1,**d2)

but couldn't find the most efficient way to merge multi-nested dicts.

I'm trying to avoid recursion. What is your proposition?

like image 401
mchfrnc Avatar asked Apr 24 '15 12:04

mchfrnc


2 Answers

Unless the depth of the dictionaries to merge is strictly limited, there's no way to avoid recursion.1) Also, there's no bultin or library function to do this (that is, none that I know of), but it's actually not all that hard. Something like this should do:

def merge(d1, d2):
    for k in d2:
        if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict):
            merge(d1[k], d2[k])
        else:
            d1[k] = d2[k]   

What this does: It iterates the keys in d2 and if the key can also be found in d1 and both are dictionaries, merge those sub-dictionaries, otherwise overwrite the value in d1 with that from d2. Note that this changes d1 and its sub-dictionaries in place, so you might want to deep-copy it before.

Or use this version to create a merged copy:

def merge_copy(d1, d2):
    return {k: merge_copy(d1[k], d2[k]) if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict) else d2[k] for k in d2}

Example:

>>> d1 = {"foo": {"bar": 23, "blub": 42}, "flub": 17}
>>> d2 = {"foo": {"bar": 100}, "flub": {"flub2": 10}, "more": {"stuff": 111}}
>>> merge(d1, d2)
>>> print d1
{'foo': {'bar': 100, 'blub': 42}, 'flub': {'flub2': 10}, 'more': {'stuff': 111}}

1) You can make it iterative, using a stack, but this will only make things more complicated and should only be done to avoid problems with maximum recursive depth.

like image 105
tobias_k Avatar answered Oct 27 '22 08:10

tobias_k


Modified version of the above merge_copy function for dicts that can be thought of as merging parent and child where you want the parent to inherit all the values from child to create a new dict.

def merge_copy(child, parent):
    '''returns parent updated with child values if exists'''
    d = {}
    for k in parent:
        if k in child and isinstance(child[k], dict) and isinstance(parent[k], dict):
            v = merge_copy(child[k], parent[k])
        elif k in child:
            v = child[k]
        else:
            v = parent[k]
        d[k] = v
    return d
like image 23
anmol Avatar answered Oct 27 '22 09:10

anmol