How to merge dictionaries of dictionaries?

后端 未结 29 2743
渐次进展
渐次进展 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:12

    Dictionaries of dictionaries merge

    As this is the canonical question (in spite of certain non-generalities) I'm providing the canonical Pythonic approach to solving this issue.

    Simplest Case: "leaves are nested dicts that end in empty dicts":

    d1 = {'a': {1: {'foo': {}}, 2: {}}}
    d2 = {'a': {1: {}, 2: {'bar': {}}}}
    d3 = {'b': {3: {'baz': {}}}}
    d4 = {'a': {1: {'quux': {}}}}
    

    This is the simplest case for recursion, and I would recommend two naive approaches:

    def rec_merge1(d1, d2):
        '''return new merged dict of dicts'''
        for k, v in d1.items(): # in Python 2, use .iteritems()!
            if k in d2:
                d2[k] = rec_merge1(v, d2[k])
        d3 = d1.copy()
        d3.update(d2)
        return d3
    
    def rec_merge2(d1, d2):
        '''update first dict with second recursively'''
        for k, v in d1.items(): # in Python 2, use .iteritems()!
            if k in d2:
                d2[k] = rec_merge2(v, d2[k])
        d1.update(d2)
        return d1
    

    I believe I would prefer the second to the first, but keep in mind that the original state of the first would have to be rebuilt from its origin. Here's the usage:

    >>> from functools import reduce # only required for Python 3.
    >>> reduce(rec_merge1, (d1, d2, d3, d4))
    {'a': {1: {'quux': {}, 'foo': {}}, 2: {'bar': {}}}, 'b': {3: {'baz': {}}}}
    >>> reduce(rec_merge2, (d1, d2, d3, d4))
    {'a': {1: {'quux': {}, 'foo': {}}, 2: {'bar': {}}}, 'b': {3: {'baz': {}}}}
    

    Complex Case: "leaves are of any other type:"

    So if they end in dicts, it's a simple case of merging the end empty dicts. If not, it's not so trivial. If strings, how do you merge them? Sets can be updated similarly, so we could give that treatment, but we lose the order in which they were merged. So does order matter?

    So in lieu of more information, the simplest approach will be to give them the standard update treatment if both values are not dicts: i.e. the second dict's value will overwrite the first, even if the second dict's value is None and the first's value is a dict with a lot of info.

    d1 = {'a': {1: 'foo', 2: None}}
    d2 = {'a': {1: None, 2: 'bar'}}
    d3 = {'b': {3: 'baz'}}
    d4 = {'a': {1: 'quux'}}
    
    from collections.abc import MutableMapping
    
    def rec_merge(d1, d2):
        '''
        Update two dicts of dicts recursively, 
        if either mapping has leaves that are non-dicts, 
        the second's leaf overwrites the first's.
        '''
        for k, v in d1.items():
            if k in d2:
                # this next check is the only difference!
                if all(isinstance(e, MutableMapping) for e in (v, d2[k])):
                    d2[k] = rec_merge(v, d2[k])
                # we could further check types and merge as appropriate here.
        d3 = d1.copy()
        d3.update(d2)
        return d3
    

    And now

    from functools import reduce
    reduce(rec_merge, (d1, d2, d3, d4))
    

    returns

    {'a': {1: 'quux', 2: 'bar'}, 'b': {3: 'baz'}}
    

    Application to the original question:

    I've had to remove the curly braces around the letters and put them in single quotes for this to be legit Python (else they would be set literals in Python 2.7+) as well as append a missing brace:

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

    and rec_merge(dict1, dict2) now returns:

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

    Which matches the desired outcome of the original question (after changing, e.g. the {A} to 'A'.)

提交回复
热议问题