Let\'s suppose that I have two dictionaries:
dic1 = { \"first\":1, \"second\":4, \"third\":8}
dic2 = { \"first\":9, \"second\":5, \"fourth\":3}
Using set and dictionary comprehension
L = [d1, d2]
dups = set(d1.keys() & d2.keys())
d = {k: [L[0][k], L[1][k]] if k in dups else i[k] for i in L for k in i}
{'first': [1, 9], 'second': [4, 5], 'third': 8, 'fourth': 3}
from copy import deepcopy
def _add_value_to_list(value, lis):
if value:
if isinstance(value, list):
lis.extend(value)
else:
lis.append(value)
else:
pass
def _merge_value(value_a, value_b):
merged_value = []
_add_value_to_list(value_a, merged_value)
_add_value_to_list(value_b, merged_value)
return merged_value
def _recursion_merge_dict(new_dic, dic_a, dic_b):
if not dic_a or not dic_b:
return new_dic
else:
if isinstance(new_dic, dict):
for k, v in new_dic.items():
new_dic[k] = _recursion_merge_dict(v, dic_a.get(k, {}), dic_b.get(k, {}))
return new_dic
else:
return _merge_value(dic_a, dic_b)
def merge_dicts(dic_a, dic_b):
new_dic = deepcopy(dic_a)
new_dic.update(dic_b)
return _recursion_merge_dict(new_dic, dic_a, dic_b)
You can use a defaultdict to hold lists, and then just append the values to them. This approach easily extends to an arbitrary number of dictionaries.
from collections import defaultdict
dd = defaultdict(list)
dics = [dic1, dic2]
for dic in dics:
for key, val in dic.iteritems(): # .items() in Python 3.
dd[key].append(val)
>>> dict(dd)
{'first': [1, 9], 'fourth': [3], 'second': [4, 5], 'third': [8]}
All of the keys with a single value are still held within a list, which is probably the best way to go. You could, however, change anything of length one into the actual value, e.g.
for key, val in dd.iteritems(): # .items() in Python 3.
if len(val) == 1
dd[key] = val[0]
In general, I would say it's bad practice to cast the values of different keys as different object types. I would simply do something like:
def merge_values(val1, val2):
if val1 is None:
return [val2]
elif val2 is None:
return [val1]
else:
return [val1, val2]
dict3 = {
key: merge_values(dic1.get(key), dic2.get(key))
for key in set(dic1).union(dic2)
}
Here's a naive solution; copy one of the dictionaries over to the result and iterate over the other dictionary's keys and values, adding lists to the result as necessary. Since there are only two dictionaries, no merged list will have more than 2 items.
dic1 = {"first": 1, "second": 4, "third": 8}
dic2 = {"first": 9, "second": 5, "fourth": 3}
dic3 = dict(dic2)
for k, v in dic1.items():
dic3[k] = [dic3[k], v] if k in dic3 else v
print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': 3, 'third': 8}
If you'd like single values to be lists (likely better design; mixed types aren't much fun to deal with) you can use:
dic3 = {k: [v] for k, v in dic2.items()}
for k, v in dic1.items():
dic3[k] = dic3[k] + [v] if k in dic3 else [v]
print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': [3], 'third': [8]}
Generalizing it to any number of dictionaries:
def merge_dicts(*dicts):
"""
>>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
{'a': [2, 3, 1], 'b': [4]}
"""
merged = {}
for d in dicts:
for k, v in d.items():
if k not in merged:
merged[k] = []
merged[k].append(v)
return merged
You can use collections.defaultdict to clean it up a bit if you don't mind the import:
from collections import defaultdict
def merge_dicts(*dicts):
"""
>>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
defaultdict(<class 'list'>, {'a': [2, 3, 1], 'b': [4]})
"""
merged = defaultdict(list)
for d in dicts:
for k, v in d.items():
merged[k].append(v)
return merged
Solution for dict of lists (adapted from @dawg):
dic1 = { "first":[1], "second":[4], "third":[8]}
dic2 = { "first":[9], "second":[5], "fourth":[3]}
dic_new={}
for k,v in list(dic1.items())+list(dic2.items()):
dic_new.setdefault(k, []).extend(v)
>>> dic_new
{'first': [1, 9], 'second': [4, 5], 'third': [8], 'fourth': [3]}