How to merge dictionaries of dictionaries?

后端 未结 29 2726
渐次进展
渐次进展 2020-11-22 05:13

I need to merge multiple dictionaries, here\'s what I have for instance:

dict1 = {1:{\"a\":{A}}, 2:{\"b\":{B}}}

dict2 = {2:{\"c\":{C}}, 3:{\"d\":{D}}


        
相关标签:
29条回答
  • 2020-11-22 06:04

    How about another answer?!? This one also avoids mutation/side effects:

    def merge(dict1, dict2):
        output = {}
    
        # adds keys from `dict1` if they do not exist in `dict2` and vice-versa
        intersection = {**dict2, **dict1}
    
        for k_intersect, v_intersect in intersection.items():
            if k_intersect not in dict1:
                v_dict2 = dict2[k_intersect]
                output[k_intersect] = v_dict2
    
            elif k_intersect not in dict2:
                output[k_intersect] = v_intersect
    
            elif isinstance(v_intersect, dict):
                v_dict2 = dict2[k_intersect]
                output[k_intersect] = merge(v_intersect, v_dict2)
    
            else:
                output[k_intersect] = v_intersect
    
        return output
    
    
    dict1 = {1:{"a":{"A"}}, 2:{"b":{"B"}}}
    dict2 = {2:{"c":{"C"}}, 3:{"d":{"D"}}}
    dict3 = {1:{"a":{"A"}}, 2:{"b":{"B"},"c":{"C"}}, 3:{"d":{"D"}}}
    
    assert dict3 == merge(dict1, dict2)
    
    0 讨论(0)
  • 2020-11-22 06:05

    This should help in merging all items from dict2 into dict1:

    for item in dict2:
        if item in dict1:
            for leaf in dict2[item]:
                dict1[item][leaf] = dict2[item][leaf]
        else:
            dict1[item] = dict2[item]
    

    Please test it and tell us whether this is what you wanted.

    EDIT:

    The above mentioned solution merges only one level, but correctly solves the example given by OP. To merge multiple levels, the recursion should be used.

    0 讨论(0)
  • 2020-11-22 06:09

    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
    
    # works
    print(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}}))
    # has conflict
    merge({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

    0 讨论(0)
  • 2020-11-22 06:09

    I have an iterative solution - works much much better with big dicts & a lot of them (for example jsons etc):

    import collections
    
    
    def merge_dict_with_subdicts(dict1: dict, dict2: dict) -> dict:
        """
        similar behaviour to builtin dict.update - but knows how to handle nested dicts
        """
        q = collections.deque([(dict1, dict2)])
        while len(q) > 0:
            d1, d2 = q.pop()
            for k, v in d2.items():
                if k in d1 and isinstance(d1[k], dict) and isinstance(v, dict):
                    q.append((d1[k], v))
                else:
                    d1[k] = v
    
        return dict1
    

    note that this will use the value in d2 to override d1, in case they are not both dicts. (same as python's dict.update())

    some tests:

    def test_deep_update():
        d = dict()
        merge_dict_with_subdicts(d, {"a": 4})
        assert d == {"a": 4}
    
        new_dict = {
            "b": {
                "c": {
                    "d": 6
                }
            }
        }
        merge_dict_with_subdicts(d, new_dict)
        assert d == {
            "a": 4,
            "b": {
                "c": {
                    "d": 6
                }
            }
        }
    
        new_dict = {
            "a": 3,
            "b": {
                "f": 7
            }
        }
        merge_dict_with_subdicts(d, new_dict)
        assert d == {
            "a": 3,
            "b": {
                "c": {
                    "d": 6
                },
                "f": 7
            }
        }
    
        # test a case where one of the dicts has dict as value and the other has something else
        new_dict = {
            'a': {
                'b': 4
            }
        }
        merge_dict_with_subdicts(d, new_dict)
        assert d['a']['b'] == 4
    
    

    I've tested with around ~1200 dicts - this method took 0.4 seconds, while the recursive solution took ~2.5 seconds.

    0 讨论(0)
  • 2020-11-22 06:10

    take a look at toolz package

    import toolz
    dict1={1:{"a":"A"},2:{"b":"B"}}
    dict2={2:{"c":"C"},3:{"d":"D"}}
    toolz.merge_with(toolz.merge,dict1,dict2)
    

    gives

    {1: {'a': 'A'}, 2: {'b': 'B', 'c': 'C'}, 3: {'d': 'D'}}
    
    0 讨论(0)
  • 2020-11-22 06:10

    hey there I also had the same problem but I though of a solution and I will post it here, in case it is also useful for others, basically merging nested dictionaries and also adding the values, for me I needed to calculate some probabilities so this one worked great:

    #used to copy a nested dict to a nested dict
    def deepupdate(target, src):
        for k, v in src.items():
            if k in target:
                for k2, v2 in src[k].items():
                    if k2 in target[k]:
                        target[k][k2]+=v2
                    else:
                        target[k][k2] = v2
            else:
                target[k] = copy.deepcopy(v)
    

    by using the above method we can merge:

    target = {'6,6': {'6,63': 1}, '63,4': {'4,4': 1}, '4,4': {'4,3': 1}, '6,63': {'63,4': 1}}

    src = {'5,4': {'4,4': 1}, '5,5': {'5,4': 1}, '4,4': {'4,3': 1}}

    and this will become: {'5,5': {'5,4': 1}, '5,4': {'4,4': 1}, '6,6': {'6,63': 1}, '63,4': {'4,4': 1}, '4,4': {'4,3': 2}, '6,63': {'63,4': 1}}

    also notice the changes here:

    target = {'6,6': {'6,63': 1}, '6,63': {'63,4': 1}, '4,4': {'4,3': 1}, '63,4': {'4,4': 1}}

    src = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '4,4': {'4,9': 1}, '3,4': {'4,4': 1}, '5,5': {'5,4': 1}}

    merge = {'5,4': {'4,4': 1}, '4,3': {'3,4': 1}, '6,63': {'63,4': 1}, '5,5': {'5,4': 1}, '6,6': {'6,63': 1}, '3,4': {'4,4': 1}, '63,4': {'4,4': 1}, '4,4': {'4,3': 1, '4,9': 1}}

    dont forget to also add the import for copy:

    import copy
    
    0 讨论(0)
提交回复
热议问题