{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6c1d39cc",
   "metadata": {},
   "source": [
    "# Dictionaries\n",
    "\n",
    "This section presents a built-in type called a `dictionary`.\n",
    "It is one of Python's best features -- and the building block of many efficient and elegant algorithms. \n",
    "\n",
    "A great example in this part is the use of dictionaries to compute the number of unique words in a book and the number of times each one appears."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ec7f178b-6145-4e06-ad43-3cdf56e0cd99",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input",
     "thebe-init",
     "runnable"
    ]
   },
   "outputs": [],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "current = Path.cwd()\n",
    "for parent in [current, *current.parents]:\n",
    "    if (parent / '_config.yml').exists():\n",
    "        project_root = parent  # ← Add project root, not chapters\n",
    "        break\n",
    "else:\n",
    "    project_root = Path.cwd().parent.parent\n",
    "\n",
    "sys.path.insert(0, str(project_root))\n",
    "\n",
    "from shared import thinkpython, diagram, jupyturtle"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{contents}\n",
    ":local:\n",
    ":depth: 2\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06778404",
   "metadata": {},
   "source": [
    "## A dictionary is a mapping\n",
    "\n",
    "- The curly braces, `{}`, represent an empty dictionary\n",
    "- A **dictionary** is like a list, but more general; meaning that:\n",
    "  - the flexibility in how you access and organize that data through key-value pairs \n",
    "  - keys are unique and can be descriptive/meaningful labels.\n",
    "\n",
    "In a list, the indices have to be integers; in a dictionary they can be (almost) any type. For example, suppose we make a list of numbers, like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66363999",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "num_lst  = [ 1, 2, 3, 4, 5 ]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02acef42",
   "metadata": {},
   "source": [
    "We can use an integer as an index to get the corresponding word."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "adab6e41",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "num_lst[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4faa20f1",
   "metadata": {},
   "source": [
    "Suppose we want to go in the other direction, and look up a word to get the corresponding integer. We can't do that with a list, but we can with a dictionary.\n",
    "We'll start by creating an empty dictionary and assigning it to `numbers`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87126bc2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "numbers = {}\n",
    "numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc153e92",
   "metadata": {},
   "source": [
    "To add items to the dictionary, we'll use square brackets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a88038d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "numbers['zero'] = 0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abd8b28b",
   "metadata": {},
   "source": [
    "This assignment adds to the dictionary an **item**, which represents the association of a **key** and a **value**.\n",
    "In this example, the key is the string `'zero'` and the value is the integer `0`.\n",
    "If we display the dictionary, we see that it contains one item, which contains a key and a value separated by a colon, `:`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c1acf4b",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56e35eca",
   "metadata": {},
   "source": [
    "We can add more items like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a47c1b7b",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "numbers['one'] = 1\n",
    "numbers['two'] = 2\n",
    "numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9970e2f4",
   "metadata": {},
   "source": [
    "Now the dictionary contains three items.\n",
    "\n",
    "To look up a key and get the corresponding value, we use the bracket operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "05b42e8e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "numbers['two']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a6baee7",
   "metadata": {},
   "source": [
    "If the key isn't in the dictionary, we get a `KeyError`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9fae0a3-7fbf-4cf5-a6ea-b269d75df809",
   "metadata": {},
   "source": [
    "```python\n",
    ">>> numbers['three']\n",
    "Traceback (most recent call last):\n",
    "  File \"<python-input-132>\", line 1, in <module>\n",
    "    numbers['three']\n",
    "    ~~~~~~~^^^^^^^^^\n",
    "KeyError: 'three'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "753e3d86",
   "metadata": {},
   "source": [
    "The `len` function works on dictionaries; it returns the number of items."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "025e3067",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "len(numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0539f590",
   "metadata": {},
   "source": [
    "In mathematical language, a dictionary represents a **mapping** from keys to values, so you can also say that each key \"maps to\" a value. In this example, each number word maps to the corresponding integer. A dictionary is represented by a box with the word \"dict\" outside and the items inside. Each item is represented by a key and an arrow pointing to a value. The quotation marks indicate that the keys here are strings, not variable names."
   ]
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-mapping-exercise-q",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "source": [
    "### Exercise: Look Up Values in a Mapping\n",
    "# Use the dictionary to look up a manager and team size.\n",
    "# Print exactly:\n",
    "# Manager: Lee\n",
    "# Team size: 8\n",
    "team = {\"manager\": \"Lee\", \"team_size\": 8, \"department\": \"Sales\"}\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-mapping-exercise-a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "source": [
    "### Solution\n",
    "team = {\"manager\": \"Lee\", \"team_size\": 8, \"department\": \"Sales\"}\n",
    "\n",
    "print(\"Manager:\", team[\"manager\"])\n",
    "print(\"Team size:\", team[\"team_size\"])"
   ],
   "execution_count": null,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Manager: Lee\n",
      "Team size: 8\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2d35310",
   "metadata": {},
   "source": [
    "## Creating dictionaries\n",
    "\n",
    "In the previous section we created an empty dictionary and added items one at a time using the bracket operator.\n",
    "Instead, we could have created the dictionary all at once like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "182ce998",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "numbers = {'zero': 0, 'one': 1, 'two': 2}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "298b6be5",
   "metadata": {},
   "source": [
    "Each item consists of a key and a value separated by a colon.\n",
    "The items are separated by commas and enclosed in curly braces.\n",
    "\n",
    "Another way to create a dictionary is to use the `dict` function.\n",
    "We can make an empty dictionary like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9dcbe1ba",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "empty = dict()\n",
    "empty                           ### {}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b588a52b",
   "metadata": {},
   "source": [
    "And we can make a copy of a dictionary like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca694a08",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "numbers_copy = dict(numbers)\n",
    "numbers_copy"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "326fc904",
   "metadata": {},
   "source": [
    "It is often useful to make a copy before performing operations that modify dictionaries."
   ]
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-create-exercise-q",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "source": [
    "### Exercise: Create a Sales Dictionary\n",
    "# Create a dictionary from the variables below.\n",
    "# Print exactly:\n",
    "# {'region': 'North', 'sales': 2400, 'target_met': True}\n",
    "region = \"North\"\n",
    "sales = 2400\n",
    "target_met = True\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-create-exercise-a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "source": [
    "### Solution\n",
    "region = \"North\"\n",
    "sales = 2400\n",
    "target_met = True\n",
    "\n",
    "record = {\"region\": region, \"sales\": sales, \"target_met\": target_met}\n",
    "\n",
    "print(record)"
   ],
   "execution_count": null,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'region': 'North', 'sales': 2400, 'target_met': True}\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3241d7f",
   "metadata": {},
   "source": [
    "## The in operator\n",
    "\n",
    "The `in` operator works on dictionaries, too; it tells you whether something appears as a *key* in the dictionary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff53138a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "'one' in numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "775557dd",
   "metadata": {},
   "source": [
    "The `in` operator does *not* check whether something appears as a value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b7ea6f9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "1 in numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e323905",
   "metadata": {},
   "source": [
    "To see whether something appears as a value in a dictionary, you can use the method `values`, which returns a sequence of values, and then use the `in` operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f79b2d64",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "1 in numbers.values()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc9b79ef",
   "metadata": {},
   "source": [
    "The items in a Python dictionary are stored in a **hash table**, which is a way of organizing data that has a remarkable property: the `in` operator takes about the same amount of time no matter how many items are in the dictionary.\n",
    "That makes it possible to write some remarkably efficient algorithms."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e89f557d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "current = Path.cwd()\n",
    "for parent in [current, *current.parents]:\n",
    "    if (parent / \"_config.yml\").exists():\n",
    "        project_root = parent\n",
    "        break\n",
    "else:\n",
    "    project_root = Path.cwd().parent.parent\n",
    "\n",
    "words_path = project_root / \"data\" / \"words.txt\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0503663c",
   "metadata": {},
   "source": [
    "To demonstrate, we'll compare two algorithms for finding pairs of words where one is the reverse of another -- like `stressed` and `desserts`.\n",
    "We'll start by reading the word list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ddd19fa6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "word_list = words_path.read_text().split()\n",
    "len(word_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e2ef3ca",
   "metadata": {},
   "source": [
    "And here's `reverse_word` from the previous chapter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b987486",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def reverse_word(word):\n",
    "    return ''.join(reversed(word))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f2b8ebd",
   "metadata": {},
   "source": [
    "The following function loops through the words in the list.\n",
    "For each one, it reverses the letters and then checks whether the reversed word is in the word list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "841b29b6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def too_slow():\n",
    "    count = 0\n",
    "    for word in word_list:\n",
    "        if reverse_word(word) in word_list:\n",
    "            count += 1\n",
    "    return count"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5d7dc78",
   "metadata": {},
   "source": [
    "This function takes more than a minute to run.\n",
    "The problem is that the `in` operator checks the words in the list one at a time, starting at the beginning.\n",
    "If it doesn't find what it's looking for -- which happens most of the time -- it has to search all the way to the end."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64eb9062",
   "metadata": {},
   "source": [
    "To measure how long a function takes, we can use `%time` which is one of Jupyter's \"built-in magic commands\".\n",
    "These commands are not part of the Python language, so they might not work in other development environments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "893d1b6e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# %time too_slow()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "462995ef",
   "metadata": {},
   "source": [
    "And the `in` operator is inside the loop, so it runs once for each word.\n",
    "Since there are more than 100,000 words in the list, and for each one we check more than 100,000 words, the total number of comparisons is the number of words squared -- roughly -- which is almost 13 billion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6af93ccf",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "len(word_list)**2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5bb6eb7",
   "metadata": {},
   "source": [
    "We can make this function much faster with a dictionary.\n",
    "The following loop creates a dictionary that contains the words as keys."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f347ab00",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "word_dict = {}\n",
    "for word in word_list:\n",
    "    word_dict[word] = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94eb25eb",
   "metadata": {},
   "source": [
    "The values in `word_dict` are all `1`, but they could be anything, because we won't ever look them up -- we will only use this dictionary to check whether a key exists.\n",
    "\n",
    "Now here's a version of the previous function that replaces `word_list` with `word_dict`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b40287d0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def much_faster():\n",
    "    count = 0\n",
    "    for word in word_dict:\n",
    "        if reverse_word(word) in word_dict:\n",
    "            count += 1\n",
    "    return count"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cabe2e08",
   "metadata": {},
   "source": [
    "This function takes less than one hundredth of a second, so it's about 10,000 times faster than the previous version."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c2c88ec",
   "metadata": {},
   "source": [
    "In general, the time it takes to find an element in a list is proportional to the length of the list.\n",
    "The time it takes to find a key in a dictionary is almost constant -- regardless of the number of items."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c1d26ff",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%time much_faster()"
   ]
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-membership-exercise-q",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "source": [
    "### Exercise: Check Dictionary Membership\n",
    "# Check whether each product code exists as a key in the inventory dictionary.\n",
    "# Print exactly:\n",
    "# A12: stocked\n",
    "# C30: missing\n",
    "inventory = {\"A12\": 15, \"B18\": 0}\n",
    "checks = [\"A12\", \"C30\"]\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-membership-exercise-a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "source": [
    "### Solution\n",
    "inventory = {\"A12\": 15, \"B18\": 0}\n",
    "checks = [\"A12\", \"C30\"]\n",
    "\n",
    "for code in checks:\n",
    "    if code in inventory:\n",
    "        status = \"stocked\"\n",
    "    else:\n",
    "        status = \"missing\"\n",
    "    print(f\"{code}: {status}\")"
   ],
   "execution_count": null,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A12: stocked\n",
      "C30: missing\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "id": "758f9e66",
   "metadata": {},
   "source": [
    "## A collection of counters\n",
    "\n",
    "Suppose you are given a string and you want to count how many times each letter appears.\n",
    "A dictionary is a good tool for this job.\n",
    "We'll start with an empty dictionary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36629436",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "counter = {}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df95633b",
   "metadata": {},
   "source": [
    "As we loop through the letters in the string, suppose we see the letter `'a'` for the first time.\n",
    "We can add it to the dictionary like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "05c3127b",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "counter['a'] = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8875b77b",
   "metadata": {},
   "source": [
    "The value `1` indicates that we have seen the letter once.\n",
    "Later, if we see the same letter again, we can increment the counter like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "859d8e63",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "counter['a'] += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa44ad78",
   "metadata": {},
   "source": [
    "Now the value associated with `'a'` is `2`, because we've seen the letter twice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2a46cc8",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "counter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "484d05d7",
   "metadata": {},
   "source": [
    "The following function uses these features to count the number of times each letter appears in a string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40fa1503",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def value_counts(string):\n",
    "    counter = {}\n",
    "    for letter in string:\n",
    "        if letter not in counter:\n",
    "            counter[letter] = 1\n",
    "        else:\n",
    "            counter[letter] += 1\n",
    "    return counter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25361b00",
   "metadata": {},
   "source": [
    "Each time through the loop, if `letter` is not in the dictionary, we create a new item with key `letter` and value `1`.\n",
    "If `letter` is already in the dictionary we increment the value associated with `letter`.\n",
    "\n",
    "Here's an example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5bb9c31",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "counter = value_counts('brontosaurus')\n",
    "counter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2316366",
   "metadata": {},
   "source": [
    "The items in `counter` show that the letter `'b'` appears once, `'r'` appears twice, and so on."
   ]
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-counters-exercise-q",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "source": [
    "### Exercise: Count Support Tickets\n",
    "# Count how many tickets came from each channel.\n",
    "# Print exactly:\n",
    "# email: 3\n",
    "# phone: 2\n",
    "# chat: 1\n",
    "channels = [\"email\", \"phone\", \"email\", \"chat\", \"phone\", \"email\"]\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-counters-exercise-a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "source": [
    "### Solution\n",
    "channels = [\"email\", \"phone\", \"email\", \"chat\", \"phone\", \"email\"]\n",
    "counts = {}\n",
    "\n",
    "for channel in channels:\n",
    "    counts[channel] = counts.get(channel, 0) + 1\n",
    "\n",
    "for channel in [\"email\", \"phone\", \"chat\"]:\n",
    "    print(f\"{channel}: {counts[channel]}\")"
   ],
   "execution_count": null,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "email: 3\n",
      "phone: 2\n",
      "chat: 1\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7efb4646",
   "metadata": {},
   "source": [
    "## Looping and dictionaries\n",
    "\n",
    "If you use a dictionary in a `for` statement, it traverses the keys of the dictionary.\n",
    "To demonstrate, let's make a dictionary that counts the letters in `'banana'`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1976b82",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "counter = value_counts('banana')\n",
    "counter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56b9edd4",
   "metadata": {},
   "source": [
    "The following loop prints the keys, which are the letters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "445c49e0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "for key in counter:\n",
    "    print(key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7766570c",
   "metadata": {},
   "source": [
    "To print the values, we can use the `values` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15ff3071",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "for value in counter.values():\n",
    "    print(value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f642315",
   "metadata": {},
   "source": [
    "To print the keys and values, we can loop through the keys and look up the corresponding values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "762ff64c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "for key in counter:\n",
    "    value = counter[key]\n",
    "    print(key, value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f8b6f25",
   "metadata": {},
   "source": [
    "In the next chapter, we'll see a more concise way to do the same thing."
   ]
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-looping-exercise-q",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "source": [
    "### Exercise: Loop Through Dictionary Items\n",
    "# Loop through the dictionary and print each region with its sales.\n",
    "# Then print the total.\n",
    "# Print exactly:\n",
    "# North: 120\n",
    "# South: 90\n",
    "# West: 150\n",
    "# Total: 360\n",
    "sales = {\"North\": 120, \"South\": 90, \"West\": 150}\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-looping-exercise-a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "source": [
    "### Solution\n",
    "sales = {\"North\": 120, \"South\": 90, \"West\": 150}\n",
    "total = 0\n",
    "\n",
    "for region, amount in sales.items():\n",
    "    total += amount\n",
    "    print(f\"{region}: {amount}\")\n",
    "\n",
    "print(\"Total:\", total)"
   ],
   "execution_count": null,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "North: 120\n",
      "South: 90\n",
      "West: 150\n",
      "Total: 360\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd65d07e",
   "metadata": {},
   "source": [
    "## Lists and dictionaries\n",
    "\n",
    "You can put a list in a dictionary as a value.\n",
    "For example, here's a dictionary that maps from the number `4` to a list of four letters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63bfdfda",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "d = {4: ['r', 'o', 'u', 's']}\n",
    "d"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b890ed42",
   "metadata": {},
   "source": [
    "But you can't put a list in a dictionary as a key.\n",
    "Here's what happens if we try."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02c1bae2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%expect TypeError\n",
    "letters = list('abcd')\n",
    "d[letters] = 4"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "233a1c4b",
   "metadata": {},
   "source": [
    "I mentioned earlier that dictionaries use hash tables, and that means that the keys have to be **hashable**.\n",
    "\n",
    "A **hash** is a function that takes a value (of any kind) and returns an integer.\n",
    "Dictionaries use these integers, called hash values, to store and look up keys.\n",
    "\n",
    "This system only works if a key is immutable, so its hash value is always the same.\n",
    "But if a key is mutable, its hash value could change, and the dictionary would not work.\n",
    "That's why keys have to be hashable, and why mutable types like lists aren't.\n",
    "\n",
    "Since dictionaries are mutable, they can't be used as keys, either.\n",
    "But they *can* be used as values."
   ]
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-list-records-exercise-q",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "source": [
    "### Exercise: Read a List of Dictionaries\n",
    "# Work with a list of employee records.\n",
    "# Print the names of employees in Sales.\n",
    "# Print exactly:\n",
    "# Sales employees: ['Ana', 'Mia']\n",
    "employees = [\n",
    "    {\"name\": \"Ana\", \"department\": \"Sales\"},\n",
    "    {\"name\": \"Bo\", \"department\": \"HR\"},\n",
    "    {\"name\": \"Mia\", \"department\": \"Sales\"},\n",
    "]\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-list-records-exercise-a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "source": [
    "### Solution\n",
    "employees = [\n",
    "    {\"name\": \"Ana\", \"department\": \"Sales\"},\n",
    "    {\"name\": \"Bo\", \"department\": \"HR\"},\n",
    "    {\"name\": \"Mia\", \"department\": \"Sales\"},\n",
    "]\n",
    "\n",
    "sales_employees = []\n",
    "for employee in employees:\n",
    "    if employee[\"department\"] == \"Sales\":\n",
    "        sales_employees.append(employee[\"name\"])\n",
    "\n",
    "print(\"Sales employees:\", sales_employees)"
   ],
   "execution_count": null,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sales employees: ['Ana', 'Mia']\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "id": "199e6f4f",
   "metadata": {},
   "source": [
    "## Accumulating a list\n",
    "\n",
    "For many programming tasks, it is useful to loop through one list or dictionary while building another.\n",
    "As an example, we'll loop through the words in `word_dict` and make a list of palindromes -- that is, words that are spelled the same backward and forward, like \"noon\" and \"rotator\".\n",
    "\n",
    "In the previous chapter, one of the exercises asked you to write a function that checks whether a word is a palindrome.\n",
    "Here's a solution that uses `reverse_word`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ad21911",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def is_palindrome(word):\n",
    "    \"\"\"Check if a word is a palindrome.\"\"\"\n",
    "    return reverse_word(word) == word"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "076abbc3",
   "metadata": {},
   "source": [
    "If we loop through the words in `word_dict`, we can count the number of palindromes like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e43d44d6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "count = 0\n",
    "\n",
    "for word in word_dict:\n",
    "    if is_palindrome(word):\n",
    "        count +=1\n",
    "        \n",
    "count"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7639a76e",
   "metadata": {},
   "source": [
    "By now, this pattern is familiar.\n",
    "\n",
    "* Before the loop, `count` is initialized to `0`.\n",
    "\n",
    "* Inside the loop, if `word` is a palindrome, we increment `count`.\n",
    "\n",
    "* When the loop ends, `count` contains the total number of palindromes.\n",
    "\n",
    "We can use a similar pattern to make a list of palindromes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9beba12",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "palindromes = []\n",
    "\n",
    "for word in word_dict:\n",
    "    if is_palindrome(word):\n",
    "        palindromes.append(word)\n",
    "\n",
    "palindromes[:10]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af67f481",
   "metadata": {},
   "source": [
    "Here's how it works:\n",
    "\n",
    "* Before the loop, `palindromes` is initialized with an empty list.\n",
    "\n",
    "* Inside the loop, if `word` is a palindrome, we append it to the end of `palindromes`.\n",
    "\n",
    "* When the loop ends, `palindromes` is a list of palindromes.\n",
    "\n",
    "In this loop, `palindromes` is used as an **accumulator**, which is a variable that collects or accumulates data during a computation.\n",
    "\n",
    "Now suppose we want to select only palindromes with seven or more letters.\n",
    "We can loop through `palindromes` and make a new list that contains only long palindromes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1816006c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "long_palindromes = []\n",
    "\n",
    "for word in palindromes:\n",
    "    if len(word) >= 7:\n",
    "        long_palindromes.append(word)\n",
    "        \n",
    "long_palindromes"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ff2bbd1",
   "metadata": {},
   "source": [
    "Looping through a list like this, selecting some elements and omitting others, is called **filtering**."
   ]
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-accumulate-exercise-q",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "source": [
    "### Exercise: Accumulate Selected Values\n",
    "# Build a list of orders worth at least 100.\n",
    "# Print exactly:\n",
    "# Large orders: [125, 210, 100]\n",
    "orders = [45, 125, 80, 210, 100]\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "ch02-dicts-accumulate-exercise-a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "source": [
    "### Solution\n",
    "orders = [45, 125, 80, 210, 100]\n",
    "large_orders = []\n",
    "\n",
    "for order in orders:\n",
    "    if order >= 100:\n",
    "        large_orders.append(order)\n",
    "\n",
    "print(\"Large orders:\", large_orders)"
   ],
   "execution_count": null,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Large orders: [125, 210, 100]\n"
     ]
    }
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
