'How to Turn 2 Dictionaries into 1 in Python?

I have 2 dictionaries:

fruit1 = {'apple': 3, 'banana': 1, 'cherry': 1}
fruit2 = {'apple': 42, 'peach': 1}

The end result I want is:

inv3 = {'apple': 45, 'banana': 1, 'cherry': 1, 'peach': 1}

I have tried this sample code so far because this output looks almost similar to what I want except it is not printing out the way I want but close:

d1 = {'apple': 3, 'orange': 1,} 
d2 = {'apple': 42, 'orange': 1}

ds = [d1, d2]
d = {}

for k in d1.keys():
    d[k] = tuple(d[k] for d in ds)
print(ds)

The output would be this way:

[{'apple': 3, 'orange': 1}, {'apple': 42, 'orange': 1}]

When I tried to enter my 2 dictionaries using the sample code:

fruit1 = {'apple': 3, 'banana': 1, 'cherry': 1}
fruit2 = {'apple': 42, 'peach': 1}      

fruit3 = [fruit1, fruit2]
d = {}
            
for k in fruit1.keys():
d[k] = tuple(d[k] for d in fruit3)
print(fruit3)

I get this error message:

Traceback (most recent call last):
  line 8, in <module>
    d[k] = tuple(d[k] for d in ds)
  line 8, in <genexpr>
    d[k] = tuple(d[k] for d in ds)
KeyError: 'banana'

My questions are:

  1. How do I get the output I intended without importing any module? I am only in Chapter 5: Dictionaries and Data Structures in Automating The Boring Stuff
  2. Why did the KeyError: 'banana' occur?

Thanks!



Solution 1:[1]

There are many ways to achieve this. Here's one:

fruit1 = {'apple': 3, 'banana': 1, 'cherry': 1}
fruit2 = {'apple': 42, 'peach': 1}

inv3 = {}

for d in fruit1, fruit2:
    for k, v in d.items():
        inv3[k] = inv3.get(k, 0) + v

print(inv3)

Output:

{'apple': 45, 'banana': 1, 'cherry': 1, 'peach': 1}

Solution 2:[2]

Generally, summing dicts should be done like this:

>>> d1 = {'a': 5}
>>> d2 = {'b': 6}
>>> d3 = { **d1, **d2 }
>>> d3
{'a': 5, 'b': 6}

In case you have repeating keys of which values you would like to sum, the snippet below is going to do the job:

#!/usr/bin/env python

def sum_dicts(*dicts: dict) -> dict:
    out = {}
    for dictionary in dicts:
        for key, value in dictionary.items():
            if key not in out:
                out[key] = value
                continue
            out[key] += value
    return out


if __name__ == '__main__':
    fruit1 = {'apple': 3, 'banana': 1, 'cherry': 1}
    fruit2 = {'apple': 42, 'peach': 1}
    print(sum_dicts(fruit1, fruit2))

Output:

{'apple': 45, 'banana': 1, 'cherry': 1, 'peach': 1}

Solution 3:[3]

Question 1. How do I get the output I intended without importing any module?

My suggestion is to create the dictionary from scratch. Let me extend from your sample code above:

fruit1 = {'apple': 3, 'banana': 1, 'cherry': 1}
fruit2 = {'apple': 42, 'peach': 1}
d = {}
for fruit in [fruit1, fruit2]:
    for k in fruit.keys():
        if k in d.keys():
            d[k] += fruit[k]
        else:
            d[k] = fruit[k]
print(d)

Output:

{'apple': 6, 'banana': 1, 'cherry': 1, 'peach': 1}

Question 2. Why did the KeyError: 'banana' occur?

The reason you get KeyError: 'banana' is when the key cannot be found.

The first tuple formed is {'apple': (3, 42)} because the key 'apple' exists in both fruit1 and fruit2. Then when the iteration goes to add 'banana', the key is found in fruit1 but cannot be found in fruit2.

So in my above code, the numbers are added if the key exists. If the key does not exist, create it.

Solution 4:[4]

You can use a try block to force the addition:

for k, v in fruit1.items():
    try:
        fruit2[k] += v
    except KeyError:
        fruit2[k] = v
>>> fruit2
{'apple': 45, 'peach': 1, 'banana': 1, 'cherry': 1}

This bases the keys off fruit2

Solution 5:[5]

When you realize that the requested functionality is structure around a Monoid (semigroup), you can express this behavior directly with some building blocks: implement the __getitem__ and keys methods for a collection of dictionaries, and you're there!


class SummedDict:
    """Represents memberwise summation of dicts."""
    
    def __init__(self, *dicts):
        self.dicts = dicts

    def get(self, key, default=0):
        return sum(d.get(key, default) for d in self.dicts)

    def __getitem__(self, key):
        return self.get(key, 0)

    def __add__(self, other: MutableMapping) -> "SummedDict":
        return SummedDict(self, other)

    def keys(self):
        return reduce(lambda ks, d: ks.union(d.keys()), self.dicts, set())


def test_dicts_can_be_summed():
    d1 = dict(a=1, b=2, c=3)
    d2 = dict(a=1, c=3)
    m = SummedDict(d1, d2)
    assert m["a"] == 2
    assert m["b"] == 2
    assert m["c"] == 6
    assert m.keys() == {"a", "b", "c"}

Solution 6:[6]

For those who can import modules: just use collections.Counter

from collections import Counter

all_the_fruits = Counter()
all_the_fruits.update(fruit1)
all_the_fruits.update(fruit2)
print(all_the_fruits)
Counter({'apple': 45, 'banana': 1, 'cherry': 1, 'peach': 1})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Albert Winestein
Solution 2 Piotr Ostrowski
Solution 3
Solution 4
Solution 5 xtofl
Solution 6 Klas Å .