Dictionary dictionaries are combined

I need to combine several dictionaries, here is what I have, for example:

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

Since A B C and D are leaves of a tree, for example {"info1":"value", "info2":"value2"}

There is an unknown level (depth) of dictionaries, it can be {2:{"c":{"z":{"y":{C}}}}}

In my case, it represents the directory / file structure with the nodes being documents, and leaves the files.

I want to combine them to get:

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

I'm not sure how easy it is to do this with Python.

+106
python dictionary merge array-merge
Aug 26 '11 at 12:44
source share
24 answers

this is actually quite complicated - especially if you want to get a useful error message when something is inconsistent when correctly accepting duplicate but consistent records (something does not give another answer here).

Assuming you don't have a huge number of records, a recursive function is the 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 content of b added to a (which is also returned). if you want to save a , you can name it as merge(dict(a), b) .

agf indicated (below) that you can 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 original answer to change the first argument; which simplifies the explanation of "reduce"]

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

+123
Aug 26 '11 at 13:08
source share

Here's an easy way to do this 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)) 

Fingerprints:

 {1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}} 
+26
Aug 26 '11 at 13:50
source share

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

 class YamlReaderError(Exception): pass def 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 YAML file merging , where I only need to deal with a subset of the possible data types. Therefore, I can ignore tuples and other objects. For me, the smart logic of merging means

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

Everything else and unforeseen circumstances lead to an error.

+20
Apr 05 '13 at 14:45
source share

Dictionary dictionaries are combined

Since this is a canonical question (despite some not general ones), I provide a canonical approach to solving this problem.

The simplest case is: "leaves are nested dicts that end with empty dicts":

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

This is the easiest 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 that I would prefer the second, but I remember that the initial state of the first should have been rebuilt from its origin. Here's the use:

 >>> 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 have any other type:"

So, if they end in dicts, this is a simple case of merging the end of empty dicts. If not, this is not so trivial. If strings, how do you combine them? Kits can be updated in a similar way, so we can give this treatment, but we are losing the order in which they were combined. So the order matters?

Thus, instead of more information, the simplest approach would be to give them standard update processing if both values ​​are not dicts: i.e. the second dict value will overwrite the first, even if the second dict value is None and the first Value is a dict with a lot of information.

 d1 = {'a': {1: 'foo', 2: None}} d2 = {'a': {1: None, 2: 'bar'}} d3 = {'b': {3: 'baz'}} d4 = {'a': {1: 'quux'}} from collections import MutableMapping def rec_merge(d1, d2): ''' Update two dicts of dicts recursively, if either mapping has leaves that are non-dicts, the second leaf overwrites the first's. ''' for k, v in d1.items(): # in Python 2, use .iteritems()! 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 for the original question:

I had to remove the curly braces around the letters and put them in single quotes so that it was legal Python (otherwise they would set literals in Python 2.7+), and also added the missing shape:

 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 corresponds to the desired result of the original question (after changing, for example, from {A} to 'A' .)

+12
Jun 06 '14 at 18:32
source share

Based on @andrew cooke. This version processes nested dicts lists and also allows updating 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
+8
Aug 12 '14 at 17:52
source share

If you have an unknown dictionary level, I would suggest a recursive function:

 def combineDicts(dictionary1, dictionary2): output = {} for item, value in dictionary1.iteritems(): if dictionary2.has_key(item): if isinstance(dictionary2[item], dict): output[item] = combineDicts(value, dictionary2.pop(item)) else: output[item] = value for item, value in dictionary2.iteritems(): output[item] = value return output 
+6
Aug 26 '11 at 1:18 p.m.
source share

Based on @andrew cooke answers. It handles nested lists better.

 def deep_merge_lists(original, incoming): """ Deep merge two lists. Modifies original. Recursively call deep merge on each correlated element of list. If item type in both elements are a. dict: Call deep_merge_dicts on both values. b. list: Recursively call deep_merge_lists on both values. c. any other type: Value is overridden. d. conflicting types: Value is overridden. If length of incoming list is more that of original then extra values are appended. """ common_length = min(len(original), len(incoming)) for idx in range(common_length): if isinstance(original[idx], dict) and isinstance(incoming[idx], dict): deep_merge_dicts(original[idx], incoming[idx]) elif isinstance(original[idx], list) and isinstance(incoming[idx], list): deep_merge_lists(original[idx], incoming[idx]) else: original[idx] = incoming[idx] for idx in range(common_length, len(incoming)): original.append(incoming[idx]) def deep_merge_dicts(original, incoming): """ Deep merge two dictionaries. Modifies original. For key conflicts if both values are: a. dict: Recursively call deep_merge_dicts on both values. b. list: Call deep_merge_lists on both values. c. any other type: Value is overridden. d. conflicting types: Value is overridden. """ for key in incoming: if key in original: if isinstance(original[key], dict) and isinstance(incoming[key], dict): deep_merge_dicts(original[key], incoming[key]) elif isinstance(original[key], list) and isinstance(incoming[key], list): deep_merge_lists(original[key], incoming[key]) else: original[key] = incoming[key] else: original[key] = incoming[key] 
+5
Jun 09 '18 at 10:23
source share

This simple recursive procedure will merge one dictionary into another, overriding conflicting keys:

 #!/usr/bin/env python2.7 def merge_dicts(dict1, dict2): """ Recursively merges dict2 into dict1 """ if not isinstance(dict1, dict) or not isinstance(dict2, dict): return dict2 for k in dict2: if k in dict1: dict1[k] = merge_dicts(dict1[k], dict2[k]) else: dict1[k] = dict2[k] return dict1 print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {2:{"c":"C"}, 3:{"d":"D"}})) print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {1:{"a":"A"}, 2:{"b":"C"}})) 

Output:

 {1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}} {1: {'a': 'A'}, 2: {'b': 'C'}} 
+4
Jul 19 '14 at 6:27
source share

overview

The following approach divides the problem of deep merging dicts into:

  1. The parameterized function merge(f)(a,b) which uses the function f to merge the two dicts a and b

  2. A recursive merge function f to be used with merge




Implementation

The function for merging two (non-nested) dicts can be written in many ways. I personally like

 def merge(f): def merge(a,b): keys = a.keys() | b.keys() return {key:f(*[a.get(key), b.get(key)]) for key in keys} return merge 



A good way to determine the appropriate recurring merge function f is to use multipledispatch, which allows you to define functions that are evaluated in different ways depending on the type of their arguments.

 from multipledispatch import dispatch #for anything that is not a dict return @dispatch(object, object) def f(a, b): return b if b is not None else a #for dicts recurse @dispatch(dict, dict) def f(a,b): return merge(f)(a,b) 



example

To merge two nested dicts, just use merge(f) for example:

 dict1 = {1:{"a":"A"},2:{"b":"B"}} dict2 = {2:{"c":"C"},3:{"d":"D"}} merge(f)(dict1, dict2) #returns {1: {'a': 'A'}, 2: {'b': 'B', 'c': 'C'}, 3: {'d': 'D'}} 



Notes:

The advantages of this approach:

  • A function is built from smaller functions, each of which does one thing, which makes the code easier to explain and verify.

  • The behavior is not hard-coded, but can be modified and expanded as necessary, which improves code reuse (see Example below).




customization

Some answers also looked at recorders containing lists of, for example, other (potentially nested) dicts. In this case, you may need to display above the lists and combine them depending on the position. This can be done by adding another definition of the merge function f :

 import itertools @dispatch(list, list) def f(a,b): return [merge(f)(*arg) for arg in itertools.zip_longest(a,b,fillvalue={})] 
+4
Apr 13 '18 at 9:28
source share

There is a slight problem with cookie andrew cookes: In some cases, it changes the second argument b when changing the returned dict. In particular, this is because of this line:

 if key in a: ... else: a[key] = b[key] 

If b[key] is a dict , it is simply assigned a , that is, any subsequent changes to this dict will affect both a and b .

 a={} b={'1':{'2':'b'}} c={'1':{'3':'c'}} merge(merge(a,b), c) # {'1': {'3': 'c', '2': 'b'}} a # {'1': {'3': 'c', '2': 'b'}} (as expected) b # {'1': {'3': 'c', '2': 'b'}} <---- c # {'1': {'3': 'c'}} (unmodified) 

To fix this, the string should be replaced with the following:

 if isinstance(b[key], dict): a[key] = clone_dict(b[key]) else: a[key] = b[key] 

Where clone_dict :

 def clone_dict(obj): clone = {} for key, value in obj.iteritems(): if isinstance(value, dict): clone[key] = clone_dict(value) else: clone[key] = value return 

However. This does not explicitly take into account list , set and other things, but I hope this illustrates the pitfalls when trying to combine dicts .

And for completeness, here is my version where you can pass some dicts :

 def merge_dicts(*args): def clone_dict(obj): clone = {} for key, value in obj.iteritems(): if isinstance(value, dict): clone[key] = clone_dict(value) else: clone[key] = value return def merge(a, b, 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 else: raise Exception('Conflict at `{path}\''.format(path='.'.join(path + [str(key)]))) else: if isinstance(b[key], dict): a[key] = clone_dict(b[key]) else: a[key] = b[key] return a return reduce(merge, args, {}) 
+3
Dec 20 '14 at 22:38
source share

In this version of the function, N number of dictionaries will be taken into account, and only dictionaries - no invalid parameters can be passed, or this will cause a TypeError. The merge itself takes into account key conflicts, and instead of rewriting the data from the dictionary further along the merge chain, it creates a set of values ​​and adds to it; no data is lost.

It may not be the most effective on the page, but it is the most thorough, and you will not lose any information when you combine your 2 into 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}

+2
Aug 24 '13 at 23:32
source share

Since dictviews supports set operations, I was able to greatly simplify jterrace's answer.

 def merge(dict1, dict2): for k in dict1.keys() - dict2.keys(): yield (k, dict1[k]) for k in dict2.keys() - dict1.keys(): yield (k, dict2[k]) for k in dict1.keys() & dict2.keys(): yield (k, dict(merge(dict1[k], dict2[k]))) 

Any attempt to combine a dict with a non-dict (technically, an object with a "keys" method and an object without a "keys" method) will raise an AttributeError. This includes both the initial function call and recursive calls. This is exactly what I wanted, so I left it. You can easily catch the attributes caused by the recursive call and then provide whatever value you like.

+2
Feb 10 '16 at 4:06
source share

This should help merge all the elements 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 check it and let us know if this is really what you wanted.

EDIT:

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

+1
Aug 26 '11 at 12:52
source share

I had two dictionaries ( a and b ), each of which could contain any number of nested dictionaries. I wanted to combine them recursively, with b taking precedence over a .

Given nested dictionaries like trees, I wanted:

  • To update a so that each path to each sheet in b is represented in a
  • To replace subtrees a , if a leaf is found in the corresponding path in b
    • Maintain the invariant that all b leaf nodes remain leaves.

The existing answers were a bit complicated to my liking and left some details on the shelf. I hacked the following which passes unit tests for my data set.

  def merge_map(a, b): if not isinstance(a, dict) or not isinstance(b, dict): return b for key in b.keys(): a[key] = merge_map(a[key], b[key]) if key in a else b[key] return a 

Example (formatted for clarity):

  a = { 1 : {'a': 'red', 'b': {'blue': 'fish', 'yellow': 'bear' }, 'c': { 'orange': 'dog'}, }, 2 : {'d': 'green'}, 3: 'e' } b = { 1 : {'b': 'white'}, 2 : {'d': 'black'}, 3: 'e' } >>> merge_map(a, b) {1: {'a': 'red', 'b': 'white', 'c': {'orange': 'dog'},}, 2: {'d': 'black'}, 3: 'e'} 

The paths in b that needed to be saved were as follows:

  • 1 -> 'b' -> 'white'
  • 2 -> 'd' -> 'black'
  • 3 -> 'e' .

a had unique and non-conflicting paths:

  • 1 -> 'a' -> 'red'
  • 1 -> 'c' -> 'orange' -> 'dog'

so that they are still represented on the combined map.

+1
May 11 '16 at 9:40
source share

Short-n-sweet:

 from collections.abc import MutableMapping as Map def nested_update(d, v): """ Nested update of dict-like 'd' with dict-like 'v'. """ for key in v: if key in d and isinstance(d[key], Map) and isinstance(v[key], Map): nested_update(d[key], v[key]) else: d[key] = v[key] 

This works like (and based on) the Python dict.update method. It returns None (you can always add return d if you want) as it updates dict d in place. The v keys will overwrite any existing keys in d (it does not try to interpret the contents of the dict).

It will also work for other ("dictate") mappings.

+1
Sep 13 '18 at 17:51
source share

In case someone wants another approach to this problem, here is my solution.

Virtues : short, declarative, and functional in style (recursive, without mutations).

Potential downside : this may not be the merge you are looking for. Refer to the documentation 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 } 
+1
May 16 '19 at 23:04
source share

You can try mergedeep .




Installation

 $ pip3 install mergedeep 

Usage

Usage
 from mergedeep import merge a = {"keyA": 1} b = {"keyB": {"sub1": 10}} c = {"keyB": {"sub2": 20}} merge(a, b, c) print(a) # {"keyA": 1, "keyB": {"sub1": 10, "sub2": 20}} 

A full list of parameters can be found in the documentation !

+1
Oct 02 '19 at 5:16
source share

Of course, the code will depend on your rules for resolving merge conflicts. Here is a version that can take an arbitrary number of arguments and recursively translate them to an arbitrary depth without using any mutation of the object. The following rules are used to resolve merge conflicts:

  • dictionaries take precedence over non-tactical values ​​( {"foo": {...}} takes precedence over {"foo": "bar"} )
  • later arguments take precedence over earlier arguments (if you combine {"a": 1} , {"a", 2} and {"a": 3} in order, the result will be {"a": 3} )
 try: from collections import Mapping except ImportError: Mapping = dict def merge_dicts(*dicts): """ Return a new dictionary that is the result of merging the arguments together. In case of conflicts, later arguments take precedence over earlier arguments. """ updated = {} # grab all keys keys = set() for d in dicts: keys = keys.union(set(d)) for key in keys: values = [d[key] for d in dicts if key in d] # which ones are mapping types? (aka dict) maps = [value for value in values if isinstance(value, Mapping)] if maps: # if we have any mapping types, call recursively to merge them updated[key] = merge_dicts(*maps) else: # otherwise, just grab the last value we have, since later arguments # take precedence over earlier arguments updated[key] = values[-1] return updated 
0
Jan 09 2018-12-21T00:
source share

I tested your solutions and decided to use this in my project:

 def mergedicts(dict1, dict2, conflict, no_conflict): for k in set(dict1.keys()).union(dict2.keys()): if k in dict1 and k in dict2: yield (k, conflict(dict1[k], dict2[k])) elif k in dict1: yield (k, no_conflict(dict1[k])) else: yield (k, no_conflict(dict2[k])) dict1 = {1:{"a":"A"}, 2:{"b":"B"}} dict2 = {2:{"c":"C"}, 3:{"d":"D"}} #this helper function allows for recursion and the use of reduce def f2(x, y): return dict(mergedicts(x, y, f2, lambda x: x)) print dict(mergedicts(dict1, dict2, f2, lambda x: x)) print dict(reduce(f2, [dict1, dict2])) 

Passing functions as parameters is key to extending the jterrace solution to behave like all other recursive solutions.

0
May 24 '12 at 18:37
source share

The easiest way I can think of:

 #!/usr/bin/python from copy import deepcopy def dict_merge(a, b): if not isinstance(b, dict): return b result = deepcopy(a) for k, v in b.iteritems(): if k in result and isinstance(result[k], dict): result[k] = dict_merge(result[k], v) else: result[k] = deepcopy(v) return result a = {1:{"a":'A'}, 2:{"b":'B'}} b = {2:{"c":'C'}, 3:{"d":'D'}} print dict_merge(a,b) 

Output:

 {1: {'a': 'A'}, 2: {'c': 'C', 'b': 'B'}, 3: {'d': 'D'}} 
0
Jan 31 '14 at 11:55
source share

I have another slightly different solution:

 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 the values ​​from the second dict, but you can easily override this, with the help of some kind of witchcraft you can even throw exceptions from it.:.)

0
Sep 25 '14 at 12:34
source share
 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
12 . '18 16:47
source share

, , , , , , , , , , :

 #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) 

, :

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} }

: {'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 }}

:

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} }

:

 import copy 
0
20 . '18 0:55
source share
 from collections import defaultdict from itertools import chain class DictHelper: @staticmethod def merge_dictionaries(*dictionaries, override=True): merged_dict = defaultdict(set) all_unique_keys = set(chain(*[list(dictionary.keys()) for dictionary in dictionaries])) # Build a set using all dict keys for key in all_unique_keys: keys_value_type = list(set(filter(lambda obj_type: obj_type != type(None), [type(dictionary.get(key, None)) for dictionary in dictionaries]))) # Establish the object type for each key, return None if key is not present in dict and remove None from final result if len(keys_value_type) != 1: raise Exception("Different objects type for same key: {keys_value_type}".format(keys_value_type=keys_value_type)) if keys_value_type[0] == list: values = list(chain(*[dictionary.get(key, []) for dictionary in dictionaries])) # Extract the value for each key merged_dict[key].update(values) elif keys_value_type[0] == dict: # Extract all dictionaries by key and enter in recursion dicts_to_merge = list(filter(lambda obj: obj != None, [dictionary.get(key, None) for dictionary in dictionaries])) merged_dict[key] = DictHelper.merge_dictionaries(*dicts_to_merge) else: # if override => get value from last dictionary else make a list of all values values = list(filter(lambda obj: obj != None, [dictionary.get(key, None) for dictionary in dictionaries])) merged_dict[key] = values[-1] if override else values return dict(merged_dict) if __name__ == '__main__': d1 = {'aaaaaaaaa': ['to short', 'to long'], 'bbbbb': ['to short', 'to long'], "cccccc": ["the is a test"]} d2 = {'aaaaaaaaa': ['field is not a bool'], 'bbbbb': ['field is not a bool']} d3 = {'aaaaaaaaa': ['filed is not a string', "to short"], 'bbbbb': ['field is not an integer']} print(DictHelper.merge_dictionaries(d1, d2, d3)) d4 = {"a": {"x": 1, "y": 2, "z": 3, "d": {"x1": 10}}} d5 = {"a": {"x": 10, "y": 20, "d": {"x2": 20}}} print(DictHelper.merge_dictionaries(d4, d5)) 

Exit:

 {'bbbbb': {'to long', 'field is not an integer', 'to short', 'field is not a bool'}, 'aaaaaaaaa': {'to long', 'to short', 'filed is not a string', 'field is not a bool'}, 'cccccc': {'the is a test'}} {'a': {'y': 20, 'd': {'x1': 10, 'x2': 20}, 'z': 3, 'x': 10}} 
0
11 . '19 8:33
source share



All Articles