0

I'm extremely new to Python and stuck with a task of the online course I'm following. My knowledge of Python is very limited.

Here is the task: ''' Write a script that takes the following two dictionaries and creates a new dictionary by combining the common keys and adding the values of duplicate keys together. Please use For Loops to iterate over these dictionaries to accomplish this task.

Example input/output:

dict_1 = {"a": 1, "b": 2, "c": 3} dict_2 = {"a": 2, "c": 4 , "d": 2}

result = {"a": 3, "b": 2, "c": 7 , "d": 2}

'''

dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3 = {}


for x, y in dict_1.items():
    for z, h in dict_2.items():
        if x == z:
            dict_3[x] = (y + h)
        else:
            dict_3[x] = (y)
            dict_3[z] = (h)

print(dict_3)

Wrong output:

{'a': 2, 'c': 3, 'd': 2, 'b': 2}

Everything is working up till the "else" condition. I'm trying to isolate only the unique occurrences of both dictionaries, but the result actually overwrites what I added to the dictionary in the condition before. Do you know a way to isolate only the single occurrences for every dictionary? I guess you could count them and add "if count is 1" condition, but I can't happen to make that work. Thanks!

korben_dallas
  • 11
  • 1
  • 2
  • Why don't you insert all of dict_1 items into dict_3 and then simply iterate over dict_2, testing if each key is already in dict_3 and then adding/inserting as needed. It would be faster and simpler. – jarmod Oct 26 '20 at 17:49
  • Your approach is wrong. You shouldn't do it in O(n*m) complexity, it's simple enough to do it in O(n+m). If you don't count copying of dict_2 to dict_3, then it will be even O(m). – VisioN Oct 26 '20 at 17:49
  • https://stackoverflow.com/questions/10461531/merge-and-sum-of-two-dictionaries – Piyush Sambhi Oct 26 '20 at 17:55

6 Answers6

3
dict_1 = {"a": 1, "b": 2, "c": 3} 
dict_2 = {"a": 2, "c": 4 , "d": 2}

key_list = {*dict_1, *dict_2}
sum ={}
for key in key_list:
 sum[key] = dict_1.get(key, 0) + dict_2.get(key, 0)
print(sum)

#{'a': 3, 'c': 7, 'd': 2, 'b': 2}
Moha
  • 38
  • 4
1

Not the most elegant or efficient solution, but an intuitive way would be to extract a list of the unique keys and then iterate over the new list of keys to extract and append the values from the two dictionaries.

dict_1 = {"a": 1, "b": 2, "c": 3} 
dict_2 = {"a": 2, "c": 4 , "d": 2}
result = {}

# Extract the unique keys from both dicts
keys = set.union(set(dict_1.keys()), set(dict_2.keys()))

# Initialize the values of the result dictionary
for key in sorted(keys):
    result[key] = 0

# Append the values of dict_1 and dict_2 to result if key is present
for key in keys:
    if key in dict_1:
        result[key] += dict_1[key]
    
    if key in dict_2:
        result[key] += dict_2[key]

print(result)

This will print: {'a': 3, 'b': 2, 'c': 7, 'd': 2}

VisioN
  • 143,310
  • 32
  • 282
  • 281
Justin
  • 136
  • 4
  • To extract unique keys just use `keys = {*dict_1, *dict_2}` – VisioN Oct 26 '20 at 19:23
  • The short version of what you're doing is: `result = {k: dict_1.get(k, 0) + dict_2.get(k, 0) for k in {*dict_1, *dict_2}}`. – VisioN Oct 26 '20 at 19:34
  • @VisioN That would be a much better and efficient way of doing it, I guess I was trying to be explicit for clarity. – Justin Oct 26 '20 at 19:35
  • In that case, you don't need to cast a set of keys to list, it doesn't make any difference. – VisioN Oct 26 '20 at 19:38
  • @VisioN You're right, I answered this really quickly without paying too much attention to what I was doing. I was more focused on trying to explain the thought process. I will make that edit though. – Justin Oct 26 '20 at 19:44
0

Perhaps collections.defaultdict would be more suited to your purposes; when there's a value that it doesn't have, it just returns a default value that you assign to it and puts it in its "actual" dictionary. Then you can just convert it back to a normal dictionary with the dict() function.

from collections import defaultdict

dict_1 = {"a": 1, "b": 2, "c": 3} 
dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3 = defaultdict(int)  # provide 0 as the default value

for k, v in dict_1.items():
    dict_3[k] += v
for k, v in dict_2.items():
    dict_3[k] += v
print(dict(dict_3))  # convert back to normal dictionary
Kevin Sheng
  • 411
  • 1
  • 5
  • 8
  • Maybe it's a bit too complicated to use `defaultdict` for an *extremely new to Python* person. The main message should be addressed to the algorithm. – VisioN Oct 26 '20 at 17:56
0
dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3={}
for key in dict_1: 
    if key in dict_2: 
        dict_3[key] = dict_2[key] + dict_1[key]
    else:
        dict_3[key]=dict_1[key]
        
for key in dict_2: 
    if key in dict_1: 
        dict_3[key] = dict_2[key] + dict_1[key]
    else:
        dict_3[key]=dict_2[key]

          
print(dict_3)
Ehtisham Ahmed
  • 387
  • 3
  • 15
0

These changes to your original code produce your desired results.

dict_1 = {"a": 1, "b": 2, "c": 3} 
dict_2 = {"a": 2, "c": 4 , "d": 2}
    
dict_3 = {}


for x, y in dict_1.items():
    if x not in dict_2 and x not in dict_3:
        dict_3[x] = y
    for z, h in dict_2.items():
        if x == z:
            dict_3[x] = y + h
        elif z not in dict_1 and z not in dict_3:
            dict_3[z] = h

print(dict_3)

Prints:

{'a': 3, 'd': 2, 'b': 2, 'c': 7}

A more concise way would be to set dict_3 to dict_1 and then iterate over dict_2.

dict_3 = dict_1.copy()

for key, val in dict_2.items():
    dict_3[key] = dict_3.get(key, 0) + val

The line dict_3.get(key, 0) gets the value for key in dict_3 if it exists in dict_3, otherwise, it supplies the value 0.

Chris Charley
  • 6,403
  • 2
  • 24
  • 26
0

If you would like to avoid the use of a loop, you could use dictionary comprehension using get with default value 0 to avoid running into KeyError:

dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}

dict_3 = {key: dict_1.get(key, 0) + dict_2.get(key, 0) for key in set(list(dict_1.keys())+list(dict_2.keys()))}
>>> {'c': 7, 'b': 2, 'd': 2, 'a': 3}

Though this is possibly unnecessarily advanced.

FinleyGibson
  • 911
  • 5
  • 18