{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "l05-c000",
   "metadata": {},
   "source": [
    "# Lecture 5 - Collections 1: Lists, sets, and dictionaries\n",
    "\n",
    "**Authors:** Fiona McNeill, Matteo Degiacomi, Rayo Verweij, and Nigel Topham\n",
    "\n",
    "Until now, our variables have held one value at a time. In this lecture we will group related values into different kinds of collections: lists, sets, and dictionaries.\n",
    "\n",
    "## Learning outcomes\n",
    "\n",
    "In this notebook, we'll discuss:\n",
    "\n",
    "- storing data in lists, sets, and dictionaries;\n",
    "- creating, accessing, and updating each type of collection;\n",
    "- looping over elements in each type of collection;\n",
    "- different ways of copying and changing each type of collection.\n",
    "\n",
    "### Using the notebook\n",
    "\n",
    "Remember, this notebook is _yours_! You can edit each cell, for example by adding new examples or changing values to see what happens. Feel free to experiment!\n",
    "\n",
    "A common approach for learning how to read and write code in a structured way is called **PRIMM**:\n",
    "1. **Predict**: before running the code, look at it - do you understand each line? What do you think will happen?\n",
    "2. **Run**: now run the code and see what output you get.\n",
    "3. **Investigate**: did it go as you thought it would? If not, can you figure out why?\n",
    "4. **Modify**: now change the code and once again try to predict the output. If you got it wrong the first time, did you get it right now?\n",
    "5. **Make**: finally, try to write your own program from scratch using the skills you learned.\n",
    "\n",
    "You can apply steps 1-4 to each code cell in the notebook and many will have prediction prompts to help you get started. In addition, there will be challenges that ask you to put everything together yourself and execute step 5. There will often be multiple ways to solve these, but each challenge has a potential answer listed.\n",
    "\n",
    "### Navigating the notebook\n",
    "\n",
    "A Jupyter notebook can be operated with your keyboard. Here is a **cheat sheet**:\n",
    "\n",
    "- To run the selected cell and move to the next cell, press <kbd>&#x21E7; Shift</kbd> + <kbd>&#x23ce; Enter</kbd>.\n",
    "- To run the selected cell and keep focus there, press <kbd>⌃ Ctrl</kbd> + <kbd>&#x23ce; Enter</kbd>.\n",
    "- To create a cell under the one currently highlighted, press <kbd>B</kbd>;\n",
    "- To delete the currently highlighted cell, press <kbd>D</kbd> twice (be careful with this one!) or press <kbd>X</kbd> to cut it;\n",
    "- To get help for a specific function, place the cursor within the function's brackets, hold <kbd>&#x21E7; Shift</kbd>, and press <kbd>&#x21E5; Tab</kbd>.\n",
    "\n",
    "Watch out: **code cells remember what happened in cells before**. So, especially in the more complicated notebooks later down the line, make sure to always run **every** cell from top to bottom, as one might rely on a piece of code that came before it! If your results become confusing, use **Kernel > Restart Kernel** and then rerun all the completed cells in order.\n",
    "\n",
    "### Further reading and practice\n",
    "\n",
    "- Official Python tutorial: [Data structures](https://docs.python.org/3/tutorial/datastructures.html) - only sections 5.1 \"More on Lists\", 5.4 \"Sets\", and 5.5 \"Dictionaries\"; the others will come in later lectures.\n",
    "- W3Schools: [Lists](https://www.w3schools.com/python/python_lists.asp), [Sets](https://www.w3schools.com/python/python_sets.asp), and [Dictionaries](https://www.w3schools.com/python/python_dictionaries.asp).\n",
    "- Helsinki Python MOOC: [Lists](https://programming-26.mooc.fi/part-4/3-lists), [Dictionary](https://programming-26.mooc.fi/part-5/3-dictionary), and [References](https://programming-26.mooc.fi/part-5/2-references) (up to and including \"Copying a list\").\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfbf5f43",
   "metadata": {},
   "source": [
    "## 1. Lists"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c001",
   "metadata": {},
   "source": [
    "A **data structure** is a type of format that stores multiple pieces of data in one object for easy and efficient access and manipulation. In this notebook, we'll discuss three data structures: lists, sets, and dictionaries.\n",
    "\n",
    "A **list** is a data structure that stores objects in a defined order.\n",
    "\n",
    "Create lists using square brackets `[]`, separating items with commas `,`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c002",
   "metadata": {},
   "outputs": [],
   "source": [
    "modules = [\"Programming\", \"Mathematics\", \"Systems\", \"Databases\", \"Networking\"]\n",
    "print(modules)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24fd9bda",
   "metadata": {},
   "source": [
    "The position that an item has in a list is called its **index**. We can access individual items in a list using square brackets and the index. \n",
    "\n",
    "<div class=\"alert alert-warning\">\n",
    "<b>Watch out: zero-based indexing</b>\n",
    "    \n",
    "*In Python, the first item in a list has index 0!* This approach is common in many other programming languages (C, Java, Rust, ...) but is not universal: there are programming languages in which the first item has index 1 (R, MATLAB, Julia, ...).\n",
    "</div>\n",
    "\n",
    "**Predict:** Which values will be printed by the following statements?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "927cca52",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(modules[0])\n",
    "print(modules[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47d32a1b",
   "metadata": {},
   "source": [
    "Python also allows us to access items counting backwards from the end of the list. The last item in a list has index -1, the penultimate item has index -2, and so forth."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6d742520",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(modules[-1])\n",
    "print(modules[-4])\n",
    "print(modules[1] == modules[-4])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81436965",
   "metadata": {},
   "source": [
    "When we try to access the item in a list that does not exist, Python raises an `IndexError`. Remember that lists start at index 0, and as such the items in a list of 3 will have the indices 0, 1, and 2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8104dfa",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(modules[6])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c003",
   "metadata": {},
   "source": [
    "We can use indexing not just to access items in a list, but also to update them by assigning new values to them.\n",
    "\n",
    "However, we cannot assign items to indices that do not exist yet, so to add items to the end of a list, we use the `.append()` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c004",
   "metadata": {},
   "outputs": [],
   "source": [
    "shopping = [\"milk\", \"bread\", \"apples\"]\n",
    "shopping[1] = \"oats\"\n",
    "shopping.append(\"tea\") # shopping[3] = \"tea\" would throw an IndexError so we use .append()\n",
    "print(shopping)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c006",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: update a route</b>\n",
    "\n",
    "Initialise a list of bus stops with the items Haymarket, Princes Street, and Waverley.\n",
    "\n",
    "Then, replace the middle stop with West End and add Leith Walk to the end.\n",
    "\n",
    "Finally, print the first and final stops on the route.\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c007",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# Write your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c008",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a solution</b></summary>\n",
    "\n",
    "```python\n",
    "stops = [\"Haymarket\", \"Princes Street\", \"Waverley\"]\n",
    "\n",
    "stops[1] = \"West End\"\n",
    "stops.append(\"Leith Walk\")\n",
    "\n",
    "print(stops[0]) # First stop\n",
    "print(stops[-1]) # Last stop\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15ff1b91",
   "metadata": {},
   "source": [
    "Lists can contain data of any type, mixed together.\n",
    "\n",
    "Lists themselves have type `list`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1bbdf37d",
   "metadata": {},
   "outputs": [],
   "source": [
    "type_smoothie = [\"one\", 2, True]\n",
    "print(type(type_smoothie))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94b587da",
   "metadata": {},
   "source": [
    "`len(list)` is a function that returns the length of `list`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e7a2acc",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(len(type_smoothie))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed20b44c",
   "metadata": {},
   "source": [
    "The `list.insert(index, item)` method allows you to add a new `item` to a `list` _at a specified `index`_, rather than just at the end like `.append()` does.\n",
    "\n",
    "Note that this does not _replace_ the item at that index: for that, we use variable assignment like we saw above. Instead, `.insert()` pushes every subsequent item up by one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24cabfa7",
   "metadata": {},
   "outputs": [],
   "source": [
    "fishes = [\"sardine\", \"mackerel\", \"cockle\", \"sturgeon\"]\n",
    "\n",
    "# Replace \"cockle\" with \"tilapia\"\n",
    "fishes[2] = \"tilapia\"\n",
    "print(fishes)\n",
    "\n",
    "# Insert \"salmon\" between \"mackerel\" and \"tilapia\"\n",
    "fishes.insert(2, \"salmon\")\n",
    "print(fishes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6535db0-2b1e-4d85-8502-c180ae5477ce",
   "metadata": {},
   "source": [
    "Use `.extend()` to combine two lists into one. Whereas `.append()` would add the entire second list as a single item to the first list (creating a list _inside_ of another list, which we're not bothering with until Lecture 12), `.extend()` adds each item from the second list individually."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13d417f7-296a-4d70-90a9-50b7089a740a",
   "metadata": {},
   "outputs": [],
   "source": [
    "more_fishes = [\"halibut\", \"haddock\"]\n",
    "fishes.extend(more_fishes)\n",
    "print(fishes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1e500d3",
   "metadata": {},
   "source": [
    "Finally, let's look at two ways to remove items from lists.\n",
    "\n",
    "First, the `.remove()` method allows you to remove a particular value from a list. If a list contains duplicates of a particular item, only the first will be removed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39f95d44",
   "metadata": {},
   "outputs": [],
   "source": [
    "classes = [\"maths\", \"pe\", \"chemistry\", \"maths\", \"literature\"]\n",
    "classes.remove(\"maths\")\n",
    "print(classes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f6b644c",
   "metadata": {},
   "source": [
    "Second, the `.pop()` method allows you to remove a value _at a particular index_. If you don't specify an index, `.pop()` will remove the last item.\n",
    "\n",
    "**Predict:** What will `classes` look like at the end of this program?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0cbb8880",
   "metadata": {},
   "outputs": [],
   "source": [
    "classes.pop(2)\n",
    "print(classes)\n",
    "\n",
    "classes.pop()\n",
    "print(classes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s01-end-try",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: tidy a table of contents</b>\n",
    "\n",
    "The below table of contents has gotten scrambled up. Perform the following:\n",
    "- Insert `\"chapter 2\"` in the right position;\n",
    "- Remove `\"appendix\"`;\n",
    "- Add `\"summary\"` to the end; and\n",
    "- Print the updated list and its length.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-s01-end-work",
   "metadata": {},
   "outputs": [],
   "source": [
    "reading = [\"chapter 1\", \"chapter 3\", \"appendix\", \"chapter 4\"]\n",
    "\n",
    "# Write your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s01-end-solution",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary><b>Click for the solution</b></summary>\n",
    "\n",
    "```python\n",
    "reading = [\"chapter 1\", \"chapter 3\", \"appendix\", \"chapter 4\"]\n",
    "\n",
    "reading.insert(1, \"chapter 2\")\n",
    "reading.remove(\"appendix\")\n",
    "reading.append(\"summary\")\n",
    "\n",
    "print(reading)\n",
    "print(len(reading))\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "770ce928",
   "metadata": {},
   "source": [
    "## 2. String indexing"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77011bcc",
   "metadata": {},
   "source": [
    "Before we move on to the next data structure, a brief note about strings.\n",
    "\n",
    "As you might remember from the previous lecture, strings are _iterables_, meaning we can loop over them. This also has two further implications: first, we can use the same syntax as we use with lists to access individual characters, and we can use the `len()` function to determine the number of characters in a string. \n",
    "\n",
    "Strings are not _actual_ lists though: methods like `pop()`, `append()`, or `insert()` cannot be used with them.\n",
    "\n",
    "**Predict:** What will the following program print?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc7c184b",
   "metadata": {},
   "outputs": [],
   "source": [
    "word = \"indexing\"\n",
    "print(word[4])\n",
    "print(len(word))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa04a908",
   "metadata": {},
   "source": [
    "## 3. Dictionaries"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c009",
   "metadata": {},
   "source": [
    "In lists, we store data in order and access them by an integer, their position in the list.\n",
    "\n",
    "In a **dictionary**, we instead access a value by a unique index called a **key**. In other words, a dictionary is a collection of **key-value pairs**.\n",
    "\n",
    "Whereas lists are defined using square brackets, dictionaries use curly braces `{}`. Accessing values still uses square brackets, however."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c010",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Defining a dictionary named 'student' with keys and values separated by a colon\n",
    "student = {\n",
    "    \"name\": \"Aisha\",\n",
    "    \"year\": 1,\n",
    "    \"programme\": \"BEng Computer Science\",\n",
    "}\n",
    "\n",
    "# Access a value using the key in square brackets \n",
    "print(student[\"name\"])\n",
    "print(student[\"year\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38572b10",
   "metadata": {},
   "source": [
    "There is no syntactic difference between whether you define a dictionary or list on one or multiple lines. You can decide what you want to use yourself depending on what you find more or less readable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f7a348b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# The following is valid code but less readable than the previous example...\n",
    "student_two = { \"name\": \"Bertus\", \"year\": 2, \"programme\": \"MInf Informatics\" }\n",
    "\n",
    "# ...whereas this list could probably fit on just one line!\n",
    "fibonacci = [\n",
    "\t1,\n",
    "\t1,\n",
    "\t2,\n",
    "\t3,\n",
    "\t5,\n",
    "\t8\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c011",
   "metadata": {},
   "source": [
    "Similarly to lists, we can update individual values in a dictionary.\n",
    "\n",
    "_Unlike_ lists, however, we can also add new values to a dictionary by using keys that did not exist in variable assignment directly. Looking up values for keys that don't exist will of course not work, and raises a `KeyError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c012",
   "metadata": {},
   "outputs": [],
   "source": [
    "# It is possible to create an empty dictionary\n",
    "prices = {}\n",
    "\n",
    "prices[\"tea\"] = 2.4 # ...and only add in key-value pairs later!\n",
    "prices[\"soup\"] = 3.5 \n",
    "\n",
    "print(prices)\n",
    "\n",
    "# However, the following would raise a KeyError if uncommented\n",
    "# prices[\"coffee\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad790e2c",
   "metadata": {},
   "source": [
    "Keys in dictionaries can be both strings and numbers. This means you can, technically, build a dictionary that functions a lot like a list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6c217d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "ceci_nest_pas_une_liste = {\n",
    "\t0: \"wait\",\n",
    "\t1: \"am I in a list?\",\n",
    "\t2: \"or in a dictionary?\",\n",
    "\t3: \"let's ask Magritte\"\n",
    "}\n",
    "print(ceci_nest_pas_une_liste[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c013",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "...though in this example, using a list would have been simpler.\n",
    "\n",
    "<div class=\"alert alert-info\">\n",
    "<b>Note: when to use a list or dictionary?</b>\n",
    "\n",
    "- Use lists when you mostly care about the position that items are going to be in, and want to easily access them by their position; and\n",
    "- Use dictionaries when you want easy access to an item _regardless_ of its position, and are happy to assign each a unique key.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ba416d7-6220-4bba-b2ec-0ff60d8871d7",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: timetabling</b>\n",
    "\n",
    "Create a dictionary mapping three course codes to room names. Then, outside of the dictionary assignment, update one room, add a fourth course, and print one selected room by its course code.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c014",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "rooms = {\n",
    "    # Add three key-value pairs\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c015",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "rooms = {\n",
    "  \"INF1-IP\": \"7GS F.21\",\n",
    "  \"INF1-CriSP\": \"AT Lecture Theatre 4\",\n",
    "  \"INF1-CG\": \"TBD\"\n",
    "}\n",
    "\n",
    "rooms[\"INF1-CG\"] = \"40GS Lecture Theatre B\"\n",
    "rooms[\"INF1-FC\"] = \"Nucleus Oak Lecture Theatre\"\n",
    "\n",
    "print(rooms[\"INF1-CriSP\"])\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "935aba39",
   "metadata": {},
   "source": [
    "In addition to accessing items directly, you can pass another dictionary to `.update()`. Each key-value pair from the new dictionary is added if its key is new, or replaces the value at that key if it already exists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7dd1da3e",
   "metadata": {},
   "outputs": [],
   "source": [
    "pets = {\n",
    "\t\"Dembi\": \"Dog\",\n",
    "\t\"Chica\": \"Guinea Pig\",\n",
    "\t\"Hamlet\": \"Crow\"\n",
    "}\n",
    "\n",
    "pets.update({ \"Dembi\": \"Shepherd\" })\n",
    "\n",
    "# Writing the method over multiple lines might help with readability\n",
    "pets.update({\n",
    "\t\"Add Your First Pet Name Here!\": \"Add Your First Pet Species Here!\",\n",
    "\t\"Add Your Second Pet Name Here!\": \"Add Your Second Pet Species Here!\"\n",
    "})\n",
    "\n",
    "print(pets)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d4c2cc5",
   "metadata": {},
   "source": [
    "Just like with lists, the `.pop()` method can be used to remove an element. When using it with a dictionary, it takes the key to be removed as its parameter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd83b5b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "pets.pop(\"Chica\") # Escaped at the first opportunity\n",
    "print(pets)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s03-end-try",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: maintain a stock list</b>\n",
    "\n",
    "Start with this dictionary:\n",
    "\n",
    "```python\n",
    "stock = {\"tea\": 12, \"coffee\": 8, \"soup\": 5}\n",
    "```\n",
    "\n",
    "Use `.update()` to change the number of coffees to `10` and add `\"cake\"` with value `6`. Then remove `\"soup\"`, print the whole dictionary, and print the number of coffees left.\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-s03-end-work",
   "metadata": {},
   "outputs": [],
   "source": [
    "stock = {\"tea\": 12, \"coffee\": 8, \"soup\": 5}\n",
    "\n",
    "# Write your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s03-end-solution",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "stock = {\"tea\": 12, \"coffee\": 8, \"soup\": 5}\n",
    "\n",
    "stock.update({\"coffee\": 10, \"cake\": 6})\n",
    "stock.pop(\"soup\")\n",
    "\n",
    "print(stock)\n",
    "print(stock[\"coffee\"])\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05cf271d",
   "metadata": {},
   "source": [
    "## 4. Sets"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c016",
   "metadata": {},
   "source": [
    "A **set** is a data structure that stores _unique items_. Trying to add an item to a set that already has that item leaves the set unchanged.\n",
    "\n",
    "Items in a set do not have an order nor an index.\n",
    "\n",
    "You define a set using curly braces `{}` like a dictionary, but without adding keys to the values.\n",
    "\n",
    "**Predict:** Which items will be present when the following set is printed? Note that because sets are unordered, its printed order may vary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c017",
   "metadata": {},
   "outputs": [],
   "source": [
    "attendees = {\"Aisha\", \"Ben\", \"Cara\", \"Aisha\"}\n",
    "print(attendees)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fab2d17",
   "metadata": {},
   "source": [
    "To create an empty set, we use the `set()` function, as empty curly braces are already used by dictionaries.\n",
    "\n",
    "To add items to a set, use `.add()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3bce4d17",
   "metadata": {},
   "outputs": [],
   "source": [
    "empty_set = set()  # {} creates an empty dictionary, not a set\n",
    "empty_set.add(\"first\")\n",
    "print(empty_set)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1325cb9",
   "metadata": {},
   "source": [
    "The `set()` function also allows us to convert lists to sets. This is a great way to filter out duplicate items from a list!\n",
    "\n",
    "We can always convert a set back to a list afterwards using `list()`. Because sets are unordered and unindexed, we cannot access or change items directly, so converting back to a list usually gives us more functionality to work with. The items might not be in the same position as they were in the original list, however!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c018",
   "metadata": {},
   "outputs": [],
   "source": [
    "repeated_tags = [\"python\", \"loops\", \"python\", \"files\", \"loops\"]\n",
    "\n",
    "unique_tags = set(repeated_tags)\n",
    "print(unique_tags)\n",
    "print(type(unique_tags))\n",
    "\n",
    "unique_tags_as_list = list(unique_tags)\n",
    "print(unique_tags_as_list)\n",
    "print(type(unique_tags_as_list))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s04-end-try",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: find unique attendees</b>\n",
    "\n",
    "Convert the below list to a set and add `\"Dara\"`. Print the number of unique attendees, then add `\"Dara\"` again and print the set and its length again to confirm that the duplicate name wasn't added.\n",
    "\n",
    "Finally, convert the set back to a list and print the type of the result.\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-s04-end-work",
   "metadata": {},
   "outputs": [],
   "source": [
    "attendees = [\"Aisha\", \"Ben\", \"Aisha\", \"Cara\", \"Ben\"]\n",
    "\n",
    "# Write your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s04-end-solution",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "attendees = [\"Aisha\", \"Ben\", \"Aisha\", \"Cara\", \"Ben\"]\n",
    "\n",
    "unique_attendees = set(attendees)\n",
    "unique_attendees.add(\"Dara\")\n",
    "print(len(unique_attendees))\n",
    "\n",
    "unique_attendees.add(\"Dara\")\n",
    "print(unique_attendees)\n",
    "print(len(unique_attendees))\n",
    "\n",
    "attendee_list = list(unique_attendees)\n",
    "print(type(attendee_list))\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a544172d",
   "metadata": {},
   "source": [
    "## 5. Collection membership and loops"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c022",
   "metadata": {},
   "source": [
    "In the last lecture, we looked at how to build loops so we can automatically repeat pieces of code. Just like strings and `range()`s, lists, sets, and dictionaries are iterables as well.\n",
    "\n",
    "For lists, the loop visits items in the order of their indices. For sets, because they're unordered, looping over them can happen in any order."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c023",
   "metadata": {},
   "outputs": [],
   "source": [
    "colours = [\"red\", \"green\", \"blue\"]\n",
    "for colour in colours:\n",
    "    print(colour)\n",
    "\n",
    "tags = {\"python\", \"loops\", \"collections\"}\n",
    "for tag in tags:\n",
    "    print(tag)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c024",
   "metadata": {},
   "source": [
    "Looping over a dictionary works slightly differently. By default, using a `for ... in` loop goes over the _keys_ of the dictionary in the order that they were added. If you want to loop over the values instead, you have to use `.values()`.\n",
    "\n",
    "Alternatively, inside of the loop, you can access the value associated with the key by accessing it as usual."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c025",
   "metadata": {},
   "outputs": [],
   "source": [
    "prices = {\"tea\": 2.4, \"coffee\": 2.8, \"soup\": 3.5}\n",
    "\n",
    "for key in prices:\n",
    "    print(key)\n",
    "\n",
    "for value in prices.values():\n",
    "    print(value)\n",
    "\n",
    "for key in prices:\n",
    "    print(f\"{key}: £{prices[key]:.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2868a82f",
   "metadata": {},
   "source": [
    "Note that dictionaries also have a `.keys()` method that, predictably, produces all keys. It's not needed when creating loops, however."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ebd2e47",
   "metadata": {},
   "outputs": [],
   "source": [
    "# These pieces of code give the exact same result!\n",
    "for key in prices.keys():\n",
    "\tprint(key)\n",
    "\n",
    "for key in prices:\n",
    "\tprint(key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c026",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: total dictionary values</b>\n",
    "\n",
    "Loop over the prices in the dictionary and accumulate their total. Then, in a separate loop, print every item whose price is below £3.\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c027",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "prices = {\"tea\": 2.4, \"coffee\": 2.8, \"soup\": 3.5}\n",
    "\n",
    "# Add two loops\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c028",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "prices = {\"tea\": 2.4, \"coffee\": 2.8, \"soup\": 3.5}\n",
    "\n",
    "total = 0\n",
    "for price in prices.values():\n",
    "    total += price\n",
    "print(total)\n",
    "\n",
    "for item in prices:\n",
    "    if prices[item] < 3:\n",
    "        print(item)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90d6bdee",
   "metadata": {},
   "source": [
    "When it comes to collections, the `in` operator is not just constrained to loops! Outside of them, it functions as the **membership operator**. That is to say, it checks whether an item is present in a collection and returns `True` or `False` accordingly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e04bacbf",
   "metadata": {},
   "outputs": [],
   "source": [
    "trilogy = [\"The Fellowship of the Ring\", \"The Two Towers\", \"The Return of the King\"]\n",
    "\n",
    "if \"The Hobbit\" in trilogy:\n",
    "\tprint(\"Wait, what?\")\n",
    "else:\n",
    "\tprint(\"It's a prequel, after all!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88314620",
   "metadata": {},
   "source": [
    "When using dictionaries, `in` only checks whether a certain key is present. If you want to check the values, you have to use `.values()` just like when you're looping over them.\n",
    "\n",
    "You can also use `not in` to check whether something is  _not_ present.\n",
    "\n",
    "**Predict:** According to our checks, is the course team complete? What additional checks might you add to the if statement?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16dd63dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "positions = {\n",
    "\t\"Fiona\": \"Course organiser\",\n",
    "\t\"Matteo\": \"Instructor\",\n",
    "\t\"Rayo\": \"Instructor\",\n",
    "\t\"Dominik\": \"TA\",\n",
    "\t\"Mario\": \"TA\"\n",
    "}\n",
    "\n",
    "if \"Fiona\" not in positions or \"TA\" not in positions.values():\n",
    "\tprint(\"Something must have gone wrong!\")\n",
    "else:\n",
    "\tprint(\"All good!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s05-end-try",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: spot missing marks</b>\n",
    "\n",
    "Below you can see a list of students as well as a dictionary with their marks. Write a program that first checks whether each student has a mark recorded. Then, print the name of each student whose mark is at least `60`.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-s05-end-work",
   "metadata": {},
   "outputs": [],
   "source": [
    "roster = [\"Aisha\", \"Ben\", \"Cara\", \"Dara\"]\n",
    "marks = {\"Aisha\": 60, \"Ben\": 0, \"Cara\": 77}\n",
    "\n",
    "# Write your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-s05-end-solution",
   "metadata": {},
   "source": [
    "<details>\n",
    "<summary><b>Click for the solution</b></summary>\n",
    "\n",
    "Note that Dara is the only missing student; while Ben's mark is 0, it is still recorded.\n",
    "\n",
    "```python\n",
    "roster = [\"Aisha\", \"Ben\", \"Cara\", \"Dara\"]\n",
    "marks = {\"Aisha\": 60, \"Ben\": 0, \"Cara\": 77}\n",
    "\n",
    "for student in roster:\n",
    "    if student not in marks:\n",
    "        print(f\"No mark recorded for {student}\")\n",
    "\n",
    "for student in marks:\n",
    "    if marks[student] >= 60:\n",
    "        print(student)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c029",
   "metadata": {},
   "source": [
    "## 6. Mutability and copying collections"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24575604",
   "metadata": {},
   "source": [
    "Now you've seen all the core syntax regarding lists, sets, and dictionaries, there's one final, more fundamental concept to cover: **mutability**.\n",
    "\n",
    "**Predict:** Look at the following program. What do you think will be printed?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c030",
   "metadata": {},
   "outputs": [],
   "source": [
    "original = [\"pen\", \"pencil\"]\n",
    "alias = original\n",
    "print(original)\n",
    "print(alias)\n",
    "\n",
    "alias.append(\"eraser\")\n",
    "print(original)\n",
    "print(alias)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4af45443",
   "metadata": {},
   "source": [
    "Were you surprised? Even though we only called `.append()` on `alias`, `original` was updated too!\n",
    "\n",
    "This is because assigning a name to a value that already has a name makes _both names point to the same object_. Therefore, when writing `alias = original`, we're making both names refer to the same list, rather than making a copy of the list. From that point onwards, we can use both names to refer to the same list, interchangeably!\n",
    "\n",
    "This behaviour is true for all types of values. However, we have not noticed it before, as collections are the first types we've seen that can be updated in such a way.\n",
    "\n",
    "The basic data types we have seen before, `str`, `int`, `float`, and `bool`, are **immutable types**. This means that their objects _cannot_ be changed after their creation.\n",
    "\n",
    "As we've already seen with string methods in lecture 2, transforming an object of an immutable type returns a result without changing the original object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a568ec4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "country = \"Scotland\"\n",
    "country.upper()\n",
    "print(country) # While upper() created a new string, we did not assign it a name\n",
    "\n",
    "country_upper = country.upper()\n",
    "print(country_upper) # Instead, we have to keep the result under a new name...\n",
    "\n",
    "country = country.upper()\n",
    "print(country) # ...or rebind the name we already had"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a4ac57a",
   "metadata": {},
   "source": [
    "By contrast, collection types such as `list`, `set`, and `dict` are **mutable**, meaning their objects can be changed after creation.\n",
    "\n",
    "As a result, most collection methods do not return a new object but rather transform the object they are called upon. This is why, in the code above, printing both `original` and `alias` showed the same output after calling `alias.append(\"eraser\")`.\n",
    "\n",
    "<div class=\"alert alert-warning\">\n",
    "<b>Watch out: mutable vs immutable types</b>\n",
    "\n",
    "This can be confusing but is simply a rule you'll need to remember. In Python,\n",
    "- `str`, `int`, `float`, and `bool` are **immutable**. Their methods, such as `.upper()`, return a _new_ object that needs to be assigned a name if you want to use it.\n",
    "- `list`, `dict`, and `set` are **mutable**. Their methods, such as `.append()`, tend to change _existing_ objects regardless of whether you assign their outputs new names or not.\n",
    "</div>\n",
    "\n",
    "One more example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a1082dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "countries = [\"England\", \"Wales\", \"Scotland\"]\n",
    "fixed_countries = countries\n",
    "\n",
    "fixed_countries.append(\"Northern Ireland\")\n",
    "countries.remove(\"Scotland\")\n",
    "\n",
    "print(countries)\n",
    "print(fixed_countries)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c031",
   "metadata": {},
   "source": [
    "As you can see, it does not matter which name is used to refer to the object as both refer to the same one, which is mutated by the list methods.\n",
    "\n",
    "What if you don't want this behaviour, though? The `.copy()` method creates a separate list, set, or dictionary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c032",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Revision note: Demonstrated that an equal copy is a distinct object before mutation.\n",
    "original = [\"red\", \"green\"]\n",
    "independent = original.copy()\n",
    "independent.append(\"blue\")\n",
    "print(original)\n",
    "print(independent)\n",
    "\n",
    "first_prices = {\"tea\": 2.4}\n",
    "revised_prices = first_prices.copy()\n",
    "revised_prices[\"tea\"] = 2.6\n",
    "print(first_prices, revised_prices)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c033",
   "metadata": {},
   "source": [
    "One caveat: `.copy()` only creates what's known as a **shallow copy** of the collection. This means that if the collection contains _other_ collections (for example, you put a list inside a dictionary), these are _not_ copied over and instead remain shared. You don't have to worry too deeply about this now; we'll look into it in more detail in Lecture 12.\n",
    "\n",
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: separate two plans</b>\n",
    "\n",
    "Create a copy of the below list, call it `tomorrow`, and add a new task **only** to the new list. Then, print both lists to confirm that the original did not change.\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-c035",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "today = [\"Finish the lecture 5 notebook\", \"Go to the CriSP tutorial\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-c036",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for the solution</b></summary>\n",
    "\n",
    "```python\n",
    "today = [\"Finish the lecture 5 notebook\", \"Go to the CriSP tutorial\"]\n",
    "\n",
    "tomorrow = today.copy() # We have to use .copy() as we don't want the original list to change\n",
    "tomorrow.append(\"Congratulate myself on completing another week at uni\")\n",
    "\n",
    "print(today)\n",
    "print(tomorrow)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-consolidation-prompt",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "## Consolidation challenge\n",
    "\n",
    "Let's wrap up by practicing what we've seen in this lecture one more time.\n",
    "\n",
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: build an open day check-in report</b>\n",
    "\n",
    "An open day team has a list of check-in codes. The first character of each code tells us which desk the visitor checked in at:\n",
    "\n",
    "```python\n",
    "bookings = [\"R17\", \"A03\", \"R17\", \"G09\", \"A03\", \"R22\"]\n",
    "desk_names = {\"R\": \"Robotics\", \"A\": \"AI\", \"G\": \"Games\"}\n",
    "```\n",
    "\n",
    "Build a report for the day _without_ changing the original list or dictionary.\n",
    "\n",
    "First, add two late check-ins, `\"S01\"` and `\"D11\"`, as well as new desks `\"S\": \"Student support\"` and `\"D\": \"Databases\"`. Then, use a loop to build both a set of visited desk names and a dictionary counting how many check-ins each desk had. Finally, print which desks had more than one check-in.\n",
    "</div>\n",
    "\n",
    "Before opening the solution, check that:\n",
    "- the original list still contains `6` codes and the original dictionary still contains `3` desks;\n",
    "- the new bookings list contains `8` codes and the new desks dictionary contains `5` desks;\n",
    "- the visited desk set contains `5` names, in any printed order;\n",
    "- the counts are `{\"Robotics\": 3, \"AI\": 2, \"Games\": 1, \"Student support\": 1, \"Databases\": 1}`; and\n",
    "- only `\"Robotics\"` and `\"AI\"` are reported as having more than one check-in.\n",
    "<br><br>\n",
    "<details>\n",
    "<summary><b>Click for a hint about the loop</b></summary>\n",
    "\n",
    "Inside the loop, start by storing the first character of the check-in code in a variable. You can then use that variable to look up the desk name, add the desk name to a set, and update a count in a dictionary.\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l05-consolidation-work",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# Your solution here!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-consolidation-solution",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details><summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "bookings = [\"R17\", \"A03\", \"R17\", \"G09\", \"A03\", \"R22\"]\n",
    "desk_names = {\"R\": \"Robotics\", \"A\": \"AI\", \"G\": \"Games\"}\n",
    "\n",
    "check_ins = bookings.copy()\n",
    "desks = desk_names.copy()\n",
    "\n",
    "check_ins.append(\"S01\")\n",
    "check_ins.append(\"D11\")\n",
    "desks.update({\"S\": \"Student support\", \"D\": \"Databases\"})\n",
    "\n",
    "visited_desks = set()\n",
    "desk_counts = {}\n",
    "\n",
    "for code in check_ins:\n",
    "    desk_code = code[0]\n",
    "    desk = desks[desk_code]\n",
    "\n",
    "    visited_desks.add(desk)\n",
    "\n",
    "    if desk in desk_counts:\n",
    "        desk_counts[desk] += 1\n",
    "    else:\n",
    "        desk_counts[desk] = 1\n",
    "\n",
    "print(bookings)\n",
    "print(desk_names)\n",
    "print(check_ins)\n",
    "print(desks)\n",
    "print(visited_desks)\n",
    "print(desk_counts)\n",
    "\n",
    "for desk in desk_counts:\n",
    "    if desk_counts[desk] > 1:\n",
    "        print(f\"{desk}: {desk_counts[desk]} check-ins\")\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l05-consolidation-summary",
   "metadata": {
    "tags": [
     "summary"
    ]
   },
   "source": [
    "## Summary\n",
    "\n",
    "Good job! There was a lot of content in this lecture so here is a recap of everything again:\n",
    "\n",
    "### Key terms\n",
    "\n",
    "<dl>\n",
    "\t<dt><b>List</b></dt>\n",
    "\t<dd>A data structure (<code>list</code>) that keeps data in order using integer indices.</dd>\n",
    "\t<dt><b>Set</b></dt>\n",
    "\t<dd>A data structure (<code>set</code>) that keeps data unordered and deduplicated.</dd>\n",
    "\t<dt><b>Dictionary</b></dt>\n",
    "\t<dd>A data structure (<code>dict</code>) that keeps data in an ordered collection of key-value pairs.</dd>\n",
    "\t<dt><b>Index</b></dt>\n",
    "\t<dd>A unique value that points to an item in a collection. In a list, this is an integer; in a dictionary, this is the key.</dd>\n",
    "    <dt><b>Key</b></dt>\n",
    "\t<dd>A value used to look up an entry in a dictionary. Can be a string or a number.</dd>\n",
    "\t<dt><b>Immutable type</b></dt>\n",
    "\t<dd>A type whose objects cannot be changed after creation. Includes <code>str</code>, <code>int</code>, <code>float</code>, and <code>bool</code>.</dd>\n",
    "\t<dt><b>Mutable type</b></dt>\n",
    "\t<dd>A type whose objects can be changed after creation. Includes <code>list</code>, <code>set</code>, and <code>dict</code>. Multiple names can refer to the same mutable object, so changes may be visible through all of them.</dd>\n",
    "\t<dt><b>Shallow copy</b></dt>\n",
    "\t<dd>A copy of a collection, created using <code>.copy()</code>, that only creates a copy of the top-level collection, leaving any nested collections to point to their original locations in memory.</dd>\n",
    "</dl>\n",
    "\n",
    "### New syntax\n",
    "\n",
    "<dl>\n",
    "\t<dt><code>[a, b, c]</code></dt>\n",
    "\t<dd>A list containing the items <code>a</code>, <code>b</code>, and <code>c</code>; indexed by the integers <code>0</code>, <code>1</code>, and <code>2</code>.</dd>\n",
    "\t<dt><code>{a, b, c}</code></dt>\n",
    "\t<dd>A set containing the items <code>a</code>, <code>b</code>, and <code>c</code>. Adding an item already present leaves the set unchanged.</dd>\n",
    "\t<dt><code>{a: 1, b: 2, c: 3}</code></dt>\n",
    "\t<dd>A dictionary containing the keys <code>a</code>, <code>b</code>, and <code>c</code>, mapping on to the values <code>1</code>, <code>2</code>, and <code>3</code>, respectively.</dd>\n",
    "\t<dt><code>list()</code></dt>\n",
    "\t<dd>A function that (when empty) creates a new list or (when passed a set) converts a set to a list.</dd>\n",
    "\t<dt><code>set()</code></dt>\n",
    "\t<dd>A function that (when empty) creates a new set or (when passed a list) converts a list to a set.</dd>\n",
    "\t<dt><code>list[index]</code>, <code>dict[key]</code>, and <code>string[index]</code></dt>\n",
    "\t<dd>Accessing a list item by its index, a dictionary value by its key, and a string character by its index. Remember, indices in Python start at 0!</dd>\n",
    "\t<dt><code>len()</code></dt>\n",
    "\t<dd>Returns the length of a string, list, set, or dictionary.</dd>\n",
    "\t<dt><code>in</code> (when used outwith <code>for</code> loops)</dt>\n",
    "\t<dd>Membership operator that checks whether an item is present in a list, set, or dictionary.</dd>\n",
    "\t<dt><code>KeyError</code></dt>\n",
    "\t<dd>A type of error that highlights when you're trying to access a non-existing key in a dictionary</dd>\n",
    "</dl>\n",
    "\n",
    "#### List methods\n",
    "<dl>\n",
    "\t<dt><code>list.append(item)</code></dt>\n",
    "\t<dd>Add <code>item</code> to the end of <code>list</code>.</dd>\n",
    "\t<dt><code>list.insert(index, item)</code></dt>\n",
    "\t<dd>Add <code>item</code> to <code>list</code> at position <code>index</code>.</dd>\n",
    "\t<dt><code>list.extend(list_2)</code></dt>\n",
    "\t<dd>Adds the items from <code>list_2</code> individually to the end of <code>list</code>.</dd>\n",
    "\t<dt><code>list.pop(index)</code></dt>\n",
    "\t<dd>Remove the item at position <code>index</code> from <code>list</code>. If not provided <code>index</code>, removes the last item in the list.</dd>\n",
    "\t<dt><code>list.remove(item)</code></dt>\n",
    "\t<dd>Removes the first occurrence of <code>item</code> from <code>list</code>.</dd>\n",
    "\t<dt><code>list.copy()</code></dt>\n",
    "\t<dd>Creates a shallow copy of <code>list</code>.</dd>\n",
    "</dl>\n",
    "\n",
    "#### Dictionary methods\n",
    "<dl>\n",
    "\t<dt><code>dict.values()</code></dt>\n",
    "\t<dd>Provides a view of the values in <code>dict</code> for iteration. It does not create a list.</dd>\n",
    "\t<dt><code>dict.update({key: value})</code></dt>\n",
    "\t<dd>Adds or updates each key-value pair from the supplied dictionary. Any new keys are added and existing keys receive the new values.</dd>\n",
    "\t<dt><code>dict.pop(key)</code></dt>\n",
    "\t<dd>Remove the key-value pair at <code>key</code> from <code>dict</code>.</dd>\n",
    "\t<dt><code>dict.copy()</code></dt>\n",
    "\t<dd>Creates a shallow copy of <code>dict</code>.</dd>\n",
    "</dl>\n",
    "\n",
    "#### Set methods\n",
    "<dl>\n",
    "\t<dt><code>set.add(item)</code></dt>\n",
    "\t<dd>Adds <code>item</code> to <code>set</code> if it is not already present.</dd>\n",
    "\t<dt><code>set.copy()</code></dt>\n",
    "\t<dd>Creates a shallow copy of <code>set</code>.</dd>\n",
    "</dl>\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
