Webair
Webair

Reputation: 105

sum up values of dictionaries

I have a dictionary such as below:

grocery={
    'James': {'Brocolli': 3, 'Carrot': 3, 'Cherry': 5},
    'Jill': {'Apples': 2, 'Carrot': 4, 'Tomatoes': 8},
    'Sunny': {'Apples': 5, 'Carrot': 2, 'Cherry': 2, 'Chicken': 3, 'Tomatoes': 6}
}
food={}
for a,b in grocery.items():
     for i,j in b.items():
          food[i]+=(b.get(i,0))

I am trying to calculate total of each food item and it is not working as expected.

For eg: I would like to count total of Carrot, total of Apples and so on.

The above code is giving me following error:

File "dictionary1.py", line 6, in <module>
      food[i]+=(b.get(i,0))
   KeyError: 'Cherry

How to sum up total of each item?

Upvotes: 1

Views: 449

Answers (3)

Martijn Pieters
Martijn Pieters

Reputation: 1121446

Your food dictionary is empty and has no keys at the start; you can't just sum up a value to something that isn't there yet.

Instead of +=, get the current value or a default, using dict.get() again:

food[i] = food.get(i, 0) + b.get(i,0)

You don't really need to use b.get() here, as you already have the values of b in the variable j:

food[i] = food.get(i, 0) + j

You could also use a collections.defaultdict() object to make keys 'automatically' exist when you try to access them, with a default value:

from collections import defaultdict

food = defaultdict(int)  # insert int() == 0 when a key is not there yet

and in the inner loop then use food[i] += j.

I strongly recommend you use better names for your variables. If you iterate over dict.values() rather than dict.items(), you can look at the values only when you don't need the keys (like for the outer for loop):

food = {}
for shopping in grocery.values():
    for name, quantity in shopping.items():
        food[name] = food.get(name, 0) + quantity

Another option is to use a dedicated counting and summing dictionary subclass, called collections.Counter(). This class directly supports summing your groceries in a single line:

from collections import Counter

food = sum(map(Counter, grocery.values()), Counter())

map(Counter, ...) creates Counter objects for each of your input dictionaries, and sum() adds up all those objects (the extra Counter() argument 'primes' the function to use an empty Counter() as a starting value rather than an integer 0).

Demo of the latter:

>>> from collections import Counter
>>> sum(map(Counter, grocery.values()), Counter())
Counter({'Tomatoes': 14, 'Carrot': 9, 'Cherry': 7, 'Apples': 7, 'Brocolli': 3, 'Chicken': 3})

A Counter is still a dictionary, just one with extra functionality. You can always go back to a dictionary by passing the Counter to dict():

>>> food = sum(map(Counter, grocery.values()), Counter())
>>> dict(food)
{'Brocolli': 3, 'Carrot': 9, 'Cherry': 7, 'Apples': 7, 'Tomatoes': 14, 'Chicken': 3}

Upvotes: 2

RetroCode
RetroCode

Reputation: 332

Simply do

from collections import defaultdict

food = defaultdict(int) <-- default value of 0 to every non existent key

..and your code should work :)

PS. You get the error because you are trying to add values to uninitialized keys... Don't assume that non existent keys start from 0...

Upvotes: 2

Thomas K&#252;hn
Thomas K&#252;hn

Reputation: 9810

You get the error, because in the beginning the keys, i.e. 'Apples', 'Tomatoes', ..., do not exist in food. You can correct this with a try-except block:

grocery={
    "Jill":{"Apples":2, "Tomatoes":8,"Carrot":4},
    "James":{"Carrot":3,"Brocolli":3,"Cherry":5},
    "Sunny":{"Chicken":3,"Apples":5,"Carrot":2,"Tomatoes":6,"Cherry":2}
}
food={}
for a,b in grocery.items():
    for i,j in b.items():
        try:
            food[i] += j
        except KeyError:
            food[i] = j

Also, you can get rid of the b.get(i,0) statement, because you already iterate through b and only get values (j) that actually exist in b.

Upvotes: 1

Related Questions