4

I am not looking for something like this:

How do I merge two dictionaries in a single expression?

Generic way of updating python dictionary without overwriting the subdictionaries

Python: Dictionary merge by updating but not overwriting if value exists

I am looking for something like this:

input:

d1 = {'a': 'a', 'b': 'b'}
d2 = {'b': 'c', 'c': 'd'}

output:

new_dict = {'a': ['a'], 'b': ['b', 'c'], 'c': ['d']}

I have the following code which works but I am wondering if there is a more efficient method:

First, I create a list "unique_vals", where all the values that are present in both dicts are stored. From this, a new dictionary is created which stores all the values present in both dictionaries

unique_vals = []
new_dict = {}
for key in list(d1.keys())+list(d2.keys()) :
    unique_vals = []
    try:
        for val in d1[key]:
            try:
                for val1 in d2[key]:
                    if(val1 == val) and (val1 not in unique_vals):
                        unique_vals.append(val)
            except:
                continue
    except:        
        new_dict[key] = unique_vals
    new_dict[key] = unique_vals

Then, for every value in both dictionaries that are not listed in this new dictionary, these values are appended to the new dictionary.

for key in d1.keys():
      for val in d1[key]:
          if val not in new_dict[key]:
              new_dict[key].append(val)
for key in d2.keys():
    for val in d2[key]:
        if val not in new_dict[key]:
            new_dict[key].append(val)
aze45sq6d
  • 876
  • 3
  • 11
  • 26

3 Answers3

3

Maybe with a defaultdict?

>>> d1 = {'a': 'a', 'b': 'b'} 
>>> d2 = {'b': 'c', 'c': 'd'}
>>> from collections import defaultdict                                         
>>>                                                                             
>>> merged = defaultdict(list)                                                  
>>> dicts = [d1, d2]                                                            
>>> for d in dicts: 
...:     for key, value in d.items(): 
...:         merged[key].append(value) 
...:                                                                            
>>> merged                                                                      
defaultdict(list, {'a': ['a'], 'b': ['b', 'c'], 'c': ['d']})

This works with any number of dictionaries in the dicts list.

As a function:

def merge_dicts(dicts):
    merged = defaultdict(list)

    for d in dicts:
        for key, value in d.items():
            merged[key].append(value)

    return merged
actual_panda
  • 1,178
  • 9
  • 27
3

Here is a far simpler version:

d1 = {'a': 'a', 'b': 'b'}
d2 = {'b': 'c', 'c': 'd'}

new_dict = {key: [value] for key, value in d1.items()}
for key, value in d2.items():
    try:
        new_dict[key].append(value)
    except:
        new_dict[key] = [value]

Output:

{'a': ['a'], 'b': ['b', 'c'], 'c': ['d']}
Mathieu
  • 5,410
  • 6
  • 28
  • 55
  • It's the same approach as @actual_panda, the difference lies in the fact that I didn't create the loop on the dictionnary but simply wrote down 2 `for`loops since you have only 2 input dictionnaries; and I took into account the possibility that a key exist in the second dictionnary and not in the first one with a try/except. – Mathieu Oct 29 '19 at 10:38
  • Thank you! I just realized I have another input. Actually, my input is as follows: d1 = {'a': ['a'], 'b': ['b', 'c']} d2 = {'b': ['d'], 'c': ['e','f']} – aze45sq6d Oct 29 '19 at 10:44
  • @aze45sq6d Then replace the `append` by an `extend` and remove the brackets from `value` since your values are already list; and it will work. – Mathieu Oct 29 '19 at 10:45
2

EDIT: Solution below is for the original question, see other answers or duplicate for updated question.

A one line solution:

def merge_dicts(*dcts):
    return {k: [d[k] for d in dcts if k in d] for k in {k for d in dcts for k in d.keys()}}

d1 = {'a': 'a', 'b': 'b'}
d2 = {'b': 'c', 'c': 'd'}
print(merge_dicts(d1, d2))
# {'c': ['d'], 'a': ['a'], 'b': ['b', 'c']}
jdehesa
  • 58,456
  • 7
  • 77
  • 121