How to merge dictionaries of dictionaries?

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

    In case someone wants yet another approach to this problem, here's my solution.

    Virtues: short, declarative, and functional in style (recursive, does no mutation).

    Potential Drawback: This might not be the merge you're looking for. Consult the docstring for semantics.

    def deep_merge(a, b):
        """
        Merge two values, with `b` taking precedence over `a`.
    
        Semantics:
        - If either `a` or `b` is not a dictionary, `a` will be returned only if
          `b` is `None`. Otherwise `b` will be returned.
        - If both values are dictionaries, they are merged as follows:
            * Each key that is found only in `a` or only in `b` will be included in
              the output collection with its value intact.
            * For any key in common between `a` and `b`, the corresponding values
              will be merged with the same semantics.
        """
        if not isinstance(a, dict) or not isinstance(b, dict):
            return a if b is None else b
        else:
            # If we're here, both a and b must be dictionaries or subtypes thereof.
    
            # Compute set of all keys in both dictionaries.
            keys = set(a.keys()) | set(b.keys())
    
            # Build output dictionary, merging recursively values with common keys,
            # where `None` is used to mean the absence of a value.
            return {
                key: deep_merge(a.get(key), b.get(key))
                for key in keys
            }
    
    0 讨论(0)
  • 2020-11-22 06:01

    Based on @andrew cooke. This version handles nested lists of dicts and also allows the option to update the values

    def merge(a, b, path=None, update=True):
        "http://stackoverflow.com/questions/7204805/python-dictionaries-of-dictionaries-merge"
        "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
                elif isinstance(a[key], list) and isinstance(b[key], list):
                    for idx, val in enumerate(b[key]):
                        a[key][idx] = merge(a[key][idx], b[key][idx], path + [str(key), str(idx)], update=update)
                elif update:
                    a[key] = b[key]
                else:
                    raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
            else:
                a[key] = b[key]
        return a
    
    0 讨论(0)
  • 2020-11-22 06:01
    class Utils(object):
    
        """
    
        >>> a = { 'first' : { 'all_rows' : { 'pass' : 'dog', 'number' : '1' } } }
        >>> b = { 'first' : { 'all_rows' : { 'fail' : 'cat', 'number' : '5' } } }
        >>> Utils.merge_dict(b, a) == { 'first' : { 'all_rows' : { 'pass' : 'dog', 'fail' : 'cat', 'number' : '5' } } }
        True
    
        >>> main = {'a': {'b': {'test': 'bug'}, 'c': 'C'}}
        >>> suply = {'a': {'b': 2, 'd': 'D', 'c': {'test': 'bug2'}}}
        >>> Utils.merge_dict(main, suply) == {'a': {'b': {'test': 'bug'}, 'c': 'C', 'd': 'D'}}
        True
    
        """
    
        @staticmethod
        def merge_dict(main, suply):
            """
            获取融合的字典,以main为主,suply补充,冲突时以main为准
            :return:
            """
            for key, value in suply.items():
                if key in main:
                    if isinstance(main[key], dict):
                        if isinstance(value, dict):
                            Utils.merge_dict(main[key], value)
                        else:
                            pass
                    else:
                        pass
                else:
                    main[key] = value
            return main
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    
    0 讨论(0)
  • 2020-11-22 06:02

    I have another slightly different solution here:

    def deepMerge(d1, d2, inconflict = lambda v1,v2 : v2) :
    ''' merge d2 into d1. using inconflict function to resolve the leaf conflicts '''
        for k in d2:
            if k in d1 : 
                if isinstance(d1[k], dict) and isinstance(d2[k], dict) :
                    deepMerge(d1[k], d2[k], inconflict)
                elif d1[k] != d2[k] :
                    d1[k] = inconflict(d1[k], d2[k])
            else :
                d1[k] = d2[k]
        return d1
    

    By default it resolves conflicts in favor of values from the second dict, but you can easily override this, with some witchery you may be able to even throw exceptions out of it. :).

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

    This version of the function will account for N number of dictionaries, and only dictionaries -- no improper parameters can be passed, or it will raise a TypeError. The merge itself accounts for key conflicts, and instead of overwriting data from a dictionary further down the merge chain, it creates a set of values and appends to that; no data is lost.

    It might not be the most effecient on the page, but it's the most thorough and you're not going to lose any information when you merge your 2 to N dicts.

    def merge_dicts(*dicts):
        if not reduce(lambda x, y: isinstance(y, dict) and x, dicts, True):
            raise TypeError, "Object in *dicts not of type dict"
        if len(dicts) < 2:
            raise ValueError, "Requires 2 or more dict objects"
    
    
        def merge(a, b):
            for d in set(a.keys()).union(b.keys()):
                if d in a and d in b:
                    if type(a[d]) == type(b[d]):
                        if not isinstance(a[d], dict):
                            ret = list({a[d], b[d]})
                            if len(ret) == 1: ret = ret[0]
                            yield (d, sorted(ret))
                        else:
                            yield (d, dict(merge(a[d], b[d])))
                    else:
                        raise TypeError, "Conflicting key:value type assignment"
                elif d in a:
                    yield (d, a[d])
                elif d in b:
                    yield (d, b[d])
                else:
                    raise KeyError
    
        return reduce(lambda x, y: dict(merge(x, y)), dicts[1:], dicts[0])
    
    print merge_dicts({1:1,2:{1:2}},{1:2,2:{3:1}},{4:4})
    

    output: {1: [1, 2], 2: {1: 2, 3: 1}, 4: 4}

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

    The following function merges b into a.

    def mergedicts(a, b):
        for key in b:
            if isinstance(a.get(key), dict) or isinstance(b.get(key), dict):
                mergedicts(a[key], b[key])
            else:
                a[key] = b[key]
        return a
    
    0 讨论(0)
提交回复
热议问题