Merge two dictionaries and keep the values for duplicate keys in Python
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
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]
Given:
dic1 = { "first":1, "second":4, "third":8}
dic2 = { "first":9, "second":5, "fourth":3}
You can use .setdefault
:
dic_new={}
for k,v in list(dic1.items())+list(dic2.items()):
dic_new.setdefault(k, []).append(v)
else:
dic_new={k:v if len(v)>1 else v[0] for k,v in dic_new.items()}
>>> dic_new
{'first': [1, 9], 'second': [4, 5], 'third': 8, 'fourth': 3}
This produces the output in question. I think that flattening the single elements lists to a different object type is an unnecessary complexity.
With the edit, this produces the desired result:
dic_new={}
for k,v in list(dic1.items())+list(dic2.items()):
dic_new.setdefault(k, []).append(v)
>>> dic_new
{'first': [1, 9], 'second': [4, 5], 'third': [8], '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}