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: dict, b: dict, 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]:
raise Exception('Conflict at ' + '.'.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:
from functools import reduce
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
Answer from andrew cooke on Stack OverflowThis 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: dict, b: dict, 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]:
raise Exception('Conflict at ' + '.'.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:
from functools import reduce
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
You could try mergedeep.
Installation
$ pip3 install mergedeep
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}}
For a full list of options, check out the docs!
The Idiomatic Way to Merge Dictionaries in Python
Nice read!
Sometimes I'd prefer the "copy and update" just because it can be read easily by novice Python devs. I guess it depends on the project.
That being said, I really like the "Dictionary unpacking" method that I learned from your post!
Thanks!
More on reddit.comMerge/update nested dictionary
dictionary - How to merge two nested dict in python? - Stack Overflow
Nested dictionary. Merging common keys and appending values to list. 0 value isn't appending. Code inside.
Videos
» pip install deepmerge
This code is to support slightly different meaning of "merge". Let's say we have a nested dictionary:
dict1 = {'employee':{'devs':{'python':{'dev1':'Roy'}}}}
dict2 = {'employee':{'devs':{'cpp':{'dev1':'Biswas'}}}}
In this case the simple loop solution returns:
{'employee': {'devs': {'cpp': {'dev1': 'Biswas'}}}}
While the "intuitive" answer should be:
{'employee': {'devs': {'python': {'dev1': 'Roy'}, 'cpp': {'dev1': 'Biswas'}}}}
This is just a simple example, the real example may be much more complex.
Below is my attempt for such a nested dictionary. It works for nested data using recursion. And it also has some restrictions. For example if dict1 and dict2 have the same value which is not dictionary, dict2 has priority. On the other hand if dict1 contains dictionary and dict2 contains value with the same key, the priority is upon dict1 and dict2 is ignored. Other restrictions will require code changes.
def merge_dict(dict1, dict2):
for key, val in dict1.items():
if type(val) == dict:
if key in dict2 and type(dict2[key] == dict):
merge_dict(dict1[key], dict2[key])
else:
if key in dict2:
dict1[key] = dict2[key]
for key, val in dict2.items():
if not key in dict1:
dict1[key] = val
return dict1
dict1 = merge_dict(dict1, dict2)
You can just update the inner dictionary.
>>> dict1 = {'employee':{'dev1': 'Roy'}}
>>> dict2 = {'employee':{'dev2': 'Biswas'}}
>>>
>>> for key in dict1:
... if key in dict2:
... dict1[key].update(dict2[key])
...
>>> dict1
{'employee': {'dev2': 'Biswas', 'dev1': 'Roy'}}
Code: https://pastebin.com/GJn74YX5
I have a nested dictionary with the data structure:
foo[count] [string variable] = value
foo = { 0 : { "a": 2, "b": 5, "c": 6},
1 : { "a": 3, "b": 8, "d": 9},
2 : { "b": 5, "d": 9, "c": 3}}I want to take common values of these dicts and combine common values to the appropriate key variable. Perhaps doable in a new dict. If the common variable is not available for that specific count, it will add a 0. All values should be the same length in the new dict. To to look like this:
{ "a": [2, 3, 0], "b": [5, 8, 5], "c":[6, 0, 3], "d": [0,9,9] }My approach:
Create newdict a defaultdict(list) so i can check if key exists and append to the list like seen above. This will be the final dict I want
newdict = defaultdict(list)
Create the following for loop to append to the new dict:
for count in foo.keys(): for variable in foo[count].keys(): if variable in foo[count].keys(): newdict[variable].append(foo[count].get(variable)) elif variable not in foo[count].keys(): newdict[variable].append(0) else: newdict[variable] = foo[count].get(variable)
My problem:
Output:
{ "a": [2, 3], "b": [5, 8, 5], "c":[6, 3], "d": [9,9] }The newdict seem to merge all the values but it seems to always go toward the first if statement
The elif block is never reached -- 0 is never appended to the list
The else block is also never reached but it seems to be appending right so might not be a big deal(?)
I spent hours and cant seem to wrap my ahead why 0 isn't appending. Any help is appreciated. Thank you in advance!