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}