0

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!

blackraven
  • 5,284
  • 7
  • 19
  • 45
  • 1
    This should help : https://www.adamsmith.haus/python/answers/how-to-add-values-from-two-dictionaries-in-python – Marius ROBERT May 17 '22 at 10:04
  • Does this answer your question? [How do I merge two dictionaries in a single expression (take union of dictionaries)?](https://stackoverflow.com/questions/38987/how-do-i-merge-two-dictionaries-in-a-single-expression-take-union-of-dictionari) – Xiidref May 17 '22 at 12:27

6 Answers6

5

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}
DarkKnight
  • 19,739
  • 3
  • 6
  • 22
  • 1
    This is probably the most elegant solution among the proposed ones, because it relies on default values in case a key is not in the dictionary. – jthulhu May 17 '22 at 10:35
  • So I understand now that I was stuck at the nested for loops. Clearer now. Thanks, @lancelot du Lac. – ayuninotayutu May 17 '22 at 13:03
3

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}
Piotr Ostrowski
  • 520
  • 5
  • 8
2

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"}
xtofl
  • 40,723
  • 12
  • 105
  • 192
2

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})
Nikolaj Š.
  • 1,457
  • 1
  • 10
  • 17
1

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

Freddy Mcloughlan
  • 4,129
  • 1
  • 13
  • 29
1

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.

blackraven
  • 5,284
  • 7
  • 19
  • 45