{"id":"dictionaries-counting","slug":"counting","title":"Counting and grouping","text":"Counting how many times each thing appears is one of the most common jobs for a dictionary. The things become the keys, and their counts the values:\n\n```python\ncolors = [\"red\", \"blue\", \"red\", \"green\", \"blue\", \"red\"]\n\ncounts = {}\nfor color in colors:\n    if color not in counts:\n        counts[color] = 0\n    counts[color] += 1\n\nprint(counts)\n```\n\n```output\n{'red': 3, 'blue': 2, 'green': 1}\n```\n\n`not in` is the opposite of `in`: it's true when the key is not in the dictionary. The first time a color comes up, it isn't a key yet, so the loop adds it with the count 0. Then, every time, its count goes up by one.\n\nWithout the `if`, the very first `+=` stops the program. `+=` reads the old value before it adds to it, and there is no old value yet:\n\n```python\ncolors = [\"red\", \"blue\"]\ncounts = {}\nfor color in colors:\n    counts[color] += 1\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 4, in <module>\n    counts[color] += 1\n    ~~~~~~^^^^^^^\nKeyError: 'red'\n```\n\n### Grouping\n\nThe same pattern can sort things into groups. The values are lists, and each thing is appended to the list of its group. Here students are grouped by their grade:\n\n```python\nresults = [[\"Ada\", 5], [\"Max\", 3], [\"Lin\", 5], [\"Kim\", 4], [\"Bo\", 3]]\n\nby_grade = {}\nfor result in results:\n    name = result[0]\n    grade = result[1]\n    if grade not in by_grade:\n        by_grade[grade] = []\n    by_grade[grade].append(name)\n\nfor grade in sorted(by_grade):\n    print(grade, by_grade[grade])\n```\n\n```output\n3 ['Max', 'Bo']\n4 ['Kim']\n5 ['Ada', 'Lin']\n```\n\nThe first time a grade comes up, it gets an empty list. Then the name goes into the list of its grade. Both patterns have the same shape: make sure the key exists, then update its value.\n","task":"Fill in the blanks so the function `letter_counts(text)` returns a dictionary with how many times each character appears in the string. For example, `letter_counts(\"banana\")` returns `{'b': 1, 'a': 3, 'n': 2}`.\n","kind":"fill","seed":"def letter_counts(text: str) -> dict:\n    counts = {}\n    for letter in text:\n        if letter [[not in]] counts:\n            counts[letter] = [[0]]\n        counts[letter] += 1\n    return counts\n\n\nif __name__ == \"__main__\":\n    print(letter_counts(\"banana\"))\n","solution":"def letter_counts(text: str) -> dict:\n    counts = {}\n    for letter in text:\n        if letter not in counts:\n            counts[letter] = 0\n        counts[letter] += 1\n    return counts\n\n\nif __name__ == \"__main__\":\n    print(letter_counts(\"banana\"))\n","hints":["The `if` should add a letter that isn't a key yet. Which operator is true when a key is missing?","A new letter starts at 0, and the next line adds one to it. The operator is `not in`."],"tests":[{"name":"letter_counts(\"banana\")","stdin":"","call":"letter_counts(\"banana\")\n","expect":"{'b': 1, 'a': 3, 'n': 2}","hidden":false,"returns":true},{"name":"letter_counts(\"mississippi\")","stdin":"","call":"letter_counts(\"mississippi\")\n","expect":"{'m': 1, 'i': 4, 's': 4, 'p': 2}","hidden":true,"returns":true},{"name":"letter_counts(\"a b\")","stdin":"","call":"letter_counts(\"a b\")\n","expect":"{'a': 1, ' ': 1, 'b': 1}","hidden":true,"returns":true}],"require":[],"forbid":[],"more":[{"task":"Write a function `group_by_length(words)` that returns a dictionary in which the keys are word lengths and the value of each length is a list of the words of that length, in their original order.\n\nFor example, `group_by_length([\"sun\", \"moon\", \"sky\", \"star\"])` returns `{3: ['sun', 'sky'], 4: ['moon', 'star']}`.\n","kind":"code","seed":"def group_by_length(words: list) -> dict:\n    pass\n\n\nif __name__ == \"__main__\":\n    print(group_by_length([\"sun\", \"moon\", \"sky\", \"star\"]))\n","solution":"def group_by_length(words: list) -> dict:\n    groups = {}\n    for word in words:\n        length = len(word)\n        if length not in groups:\n            groups[length] = []\n        groups[length].append(word)\n    return groups\n\n\nif __name__ == \"__main__\":\n    print(group_by_length([\"sun\", \"moon\", \"sky\", \"star\"]))\n","hints":["The key for a word is `len(word)`, and the value is a list of words.","When a length comes up for the first time, give it an empty list: `groups[length] = []`.","Then append the word to its group's list: `groups[length].append(word)`."],"tests":[{"name":"group_by_length([\"sun\", \"moon\", \"sky\", \"star\"])","stdin":"","call":"group_by_length([\"sun\", \"moon\", \"sky\", \"star\"])\n","expect":"{3: ['sun', 'sky'], 4: ['moon', 'star']}","hidden":false,"returns":true},{"name":"group_by_length([\"a\", \"bb\", \"c\", \"ddd\", \"ee\"])","stdin":"","call":"group_by_length([\"a\", \"bb\", \"c\", \"ddd\", \"ee\"])\n","expect":"{1: ['a', 'c'], 2: ['bb', 'ee'], 3: ['ddd']}","hidden":true,"returns":true},{"name":"group_by_length([\"python\"])","stdin":"","call":"group_by_length([\"python\"])\n","expect":"{6: ['python']}","hidden":true,"returns":true}],"require":[],"forbid":[]},{"task":"Write a program that counts votes. It asks for votes with the prompt `Vote: ` until the user types an empty line. Then it prints each option with its number of votes, in the order the options first got a vote. An example run:\n\n```\nVote: pizza\nVote: tacos\nVote: pizza\nVote:\npizza: 2\ntacos: 1\n```\n","kind":"code","seed":"votes = {}\n","solution":"votes = {}\n\nwhile True:\n    vote = input(\"Vote: \")\n    if vote == \"\":\n        break\n    if vote not in votes:\n        votes[vote] = 0\n    votes[vote] += 1\n\nfor option, count in votes.items():\n    print(f\"{option}: {count}\")\n","hints":["Read votes in a `while True` loop and `break` when the input is the empty string `\"\"`.","Count each vote as in the lesson: if it isn't a key yet, give it the value 0, then add one.","After the loop, go through `votes.items()` and print each option and its count."],"tests":[{"name":"Test 1","stdin":"pizza\ntacos\npizza\n\n","expect":"Vote: pizza\nVote: tacos\nVote: pizza\nVote:\npizza: 2\ntacos: 1","hidden":false},{"name":"Test 2","stdin":"yes\nno\nno\nmaybe\nno\nyes\n\n","expect":"Vote: yes\nVote: no\nVote: no\nVote: maybe\nVote: no\nVote: yes\nVote:\nyes: 2\nno: 3\nmaybe: 1","hidden":true},{"name":"Test 3","stdin":"tea\n\n","expect":"Vote: tea\nVote:\ntea: 1","hidden":true}],"require":[],"forbid":[]}]}