How to merge dictionaries of dictionaries? How to merge dictionaries of dictionaries? python python

How to merge dictionaries of dictionaries?


this is actually quite tricky - particularly if you want a useful error message when things are inconsistent, while correctly accepting duplicate but consistent entries (something no other answer here does....)

assuming you don't have huge numbers of entries a recursive function is easiest:

def merge(a, b, path=None):    "merges b into a"    if path is None: path = []    for key in b:        if key in a:            if isinstance(a[key], dict) and isinstance(b[key], dict):                merge(a[key], b[key], path + [str(key)])            elif a[key] == b[key]:                pass # same leaf value            else:                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))        else:            a[key] = b[key]    return a# worksprint(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}}))# has conflictmerge({1:{"a":"A"},2:{"b":"B"}}, {1:{"a":"A"},2:{"b":"C"}})

note that this mutates a - the contents of b are added to a (which is also returned). if you want to keep a you could call it like merge(dict(a), b).

agf pointed out (below) that you may have more than two dicts, in which case you can use:

reduce(merge, [dict1, dict2, dict3...])

where everything will be added to dict1.

[note - i edited my initial answer to mutate the first argument; that makes the "reduce" easier to explain]

ps in python 3, you will also need from functools import reduce


Here's an easy way to do it using generators:

def mergedicts(dict1, dict2):    for k in set(dict1.keys()).union(dict2.keys()):        if k in dict1 and k in dict2:            if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):                yield (k, dict(mergedicts(dict1[k], dict2[k])))            else:                # If one of the values is not a dict, you can't continue merging it.                # Value from second dict overrides one in first and we move on.                yield (k, dict2[k])                # Alternatively, replace this with exception raiser to alert you of value conflicts        elif k in dict1:            yield (k, dict1[k])        else:            yield (k, dict2[k])dict1 = {1:{"a":"A"},2:{"b":"B"}}dict2 = {2:{"c":"C"},3:{"d":"D"}}print dict(mergedicts(dict1,dict2))

This prints:

{1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}}


One issue with this question is that the values of the dict can be arbitrarily complex pieces of data. Based upon these and other answers I came up with this code:

class YamlReaderError(Exception):    passdef data_merge(a, b):    """merges b into a and return merged result    NOTE: tuples and arbitrary objects are not handled as it is totally ambiguous what should happen"""    key = None    # ## debug output    # sys.stderr.write("DEBUG: %s to %s\n" %(b,a))    try:        if a is None or isinstance(a, str) or isinstance(a, unicode) or isinstance(a, int) or isinstance(a, long) or isinstance(a, float):            # border case for first run or if a is a primitive            a = b        elif isinstance(a, list):            # lists can be only appended            if isinstance(b, list):                # merge lists                a.extend(b)            else:                # append to list                a.append(b)        elif isinstance(a, dict):            # dicts must be merged            if isinstance(b, dict):                for key in b:                    if key in a:                        a[key] = data_merge(a[key], b[key])                    else:                        a[key] = b[key]            else:                raise YamlReaderError('Cannot merge non-dict "%s" into dict "%s"' % (b, a))        else:            raise YamlReaderError('NOT IMPLEMENTED "%s" into "%s"' % (b, a))    except TypeError, e:        raise YamlReaderError('TypeError "%s" in key "%s" when merging "%s" into "%s"' % (e, key, b, a))    return a

My use case is merging YAML files where I only have to deal with a subset of possible data types. Hence I can ignore tuples and other objects. For me a sensible merge logic means

  • replace scalars
  • append lists
  • merge dicts by adding missing keys and updating existing keys

Everything else and the unforeseens results in an error.