Reputation: 105
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
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
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
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