{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Slicing and unpacking\n",
    "\n",
    "In this tutorial, you will learn advanced techniques for extracting and selecting data from sequences using slicing and unpacking.\n",
    "\n",
    "**Time commitment:** 15–20 minutes\n",
    "\n",
    "**Prerequisites:**\n",
    "\n",
    "- Python 3.12 or later installed on your machine\n",
    "- Completed tutorials 01 through 05 (lists, tuples, dictionaries, sets, and comprehensions)\n",
    "\n",
    "## Learning objectives\n",
    "\n",
    "By the end of this tutorial, you will be able to:\n",
    "\n",
    "- Use slicing with a step value to select items at intervals\n",
    "- Reverse sequences using slicing\n",
    "- Assign to slices to replace parts of a list\n",
    "- Use extended unpacking with the `*` operator\n",
    "- Apply practical patterns combining slicing and unpacking"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Review of basic slicing\n",
    "\n",
    "You learned the basics of slicing in [Working with lists](https://agilearn.co.uk/guides/data-structures/learn/01-lists). The syntax `sequence[start:stop]` extracts items from `start` up to (but not including) `stop`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
    "\n",
    "print(numbers[2:5])   # Items at index 2, 3, 4\n",
    "print(numbers[:4])    # First four items\n",
    "print(numbers[6:])    # Everything from index 6 onwards"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Slicing with a step\n",
    "\n",
    "The full slicing syntax is `sequence[start:stop:step]`. The **step** value controls how many items to skip between selections:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
    "\n",
    "print(numbers[::2])    # Every second item\n",
    "print(numbers[1::2])   # Every second item, starting from index 1\n",
    "print(numbers[::3])    # Every third item"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reversing sequences\n",
    "\n",
    "A step of `-1` reverses the sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [1, 2, 3, 4, 5]\n",
    "print(numbers[::-1])\n",
    "\n",
    "word = \"Python\"\n",
    "print(word[::-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Slicing strings\n",
    "\n",
    "Slicing works the same way on strings as it does on lists and tuples:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "message = \"Hello, World!\"\n",
    "\n",
    "print(message[:5])     # \"Hello\"\n",
    "print(message[7:])     # \"World!\"\n",
    "print(message[::2])    # Every second character"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Assigning to slices\n",
    "\n",
    "With lists (but not tuples or strings, which are immutable), you can assign to a slice to replace part of the list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "colours = [\"red\", \"green\", \"blue\", \"yellow\", \"purple\"]\n",
    "\n",
    "# Replace items at index 1 and 2\n",
    "colours[1:3] = [\"lime\", \"cyan\"]\n",
    "print(colours)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The replacement does not need to be the same length as the slice:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "letters = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n",
    "\n",
    "# Replace two items with three\n",
    "letters[1:3] = [\"x\", \"y\", \"z\"]\n",
    "print(letters)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic unpacking\n",
    "\n",
    "Unpacking assigns each item in a sequence to a separate variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "coordinates = [51.5074, -0.1278, 11.0]\n",
    "latitude, longitude, elevation = coordinates\n",
    "\n",
    "print(f\"Latitude: {latitude}\")\n",
    "print(f\"Longitude: {longitude}\")\n",
    "print(f\"Elevation: {elevation} metres\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extended unpacking with `*`\n",
    "\n",
    "The `*` operator collects remaining items into a list. This is particularly useful when you want the first or last items separately:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scores = [95, 87, 92, 78, 88, 91]\n",
    "\n",
    "first, *middle, last = scores\n",
    "print(f\"First: {first}\")\n",
    "print(f\"Middle: {middle}\")\n",
    "print(f\"Last: {last}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "head, *tail = [1, 2, 3, 4, 5]\n",
    "print(f\"Head: {head}\")\n",
    "print(f\"Tail: {tail}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unpacking in function calls\n",
    "\n",
    "You can use `*` to unpack a list into function arguments and `**` to unpack a dictionary into keyword arguments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def greet(first_name, last_name, city):\n",
    "    print(f\"Hello, {first_name} {last_name} from {city}!\")\n",
    "\n",
    "# Unpack a list into positional arguments\n",
    "details = [\"Alice\", \"Smith\", \"London\"]\n",
    "greet(*details)\n",
    "\n",
    "# Unpack a dictionary into keyword arguments\n",
    "info = {\"first_name\": \"Bob\", \"last_name\": \"Jones\", \"city\": \"Manchester\"}\n",
    "greet(**info)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Swapping variables\n",
    "\n",
    "Unpacking provides an elegant way to swap variable values without a temporary variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = \"first\"\n",
    "b = \"second\"\n",
    "\n",
    "a, b = b, a\n",
    "\n",
    "print(f\"a = {a}\")\n",
    "print(f\"b = {b}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Practical patterns\n",
    "\n",
    "### Splitting a list into chunks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = list(range(1, 11))\n",
    "chunk_size = 3\n",
    "\n",
    "chunks = [data[i:i + chunk_size] for i in range(0, len(data), chunk_size)]\n",
    "print(chunks)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Getting the first and last items"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "items = [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\"]\n",
    "\n",
    "first, *_, last = items\n",
    "print(f\"First: {first}, Last: {last}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The underscore `_` is a convention for values you do not need."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise: weekly temperatures\n",
    "\n",
    "You have a list of daily temperatures (in degrees Celsius) for a week, from Monday to Sunday:\n",
    "\n",
    "```python\n",
    "temperatures = [12.5, 14.0, 13.5, 15.0, 16.5, 18.0, 17.5]\n",
    "```\n",
    "\n",
    "Using slicing and unpacking:\n",
    "\n",
    "1. Use slicing to get the weekday temperatures (Monday to Friday) and weekend temperatures (Saturday and Sunday)\n",
    "2. Use unpacking to assign the first day and last day temperatures to separate variables\n",
    "3. Find the highest and lowest temperatures across the whole week"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Write your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Solution\n",
    "\n",
    "Here is one way to complete the exercise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "temperatures = [12.5, 14.0, 13.5, 15.0, 16.5, 18.0, 17.5]\n",
    "\n",
    "# 1. Weekday and weekend temperatures\n",
    "weekdays = temperatures[:5]\n",
    "weekend = temperatures[5:]\n",
    "print(f\"Weekdays: {weekdays}\")\n",
    "print(f\"Weekend: {weekend}\")\n",
    "\n",
    "# 2. First and last day\n",
    "monday, *_, sunday = temperatures\n",
    "print(f\"Monday: {monday}°C\")\n",
    "print(f\"Sunday: {sunday}°C\")\n",
    "\n",
    "# 3. Highest and lowest\n",
    "print(f\"Highest: {max(temperatures)}°C\")\n",
    "print(f\"Lowest: {min(temperatures)}°C\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "In this tutorial, you learned how to:\n",
    "\n",
    "- Use slicing with a step value `[start:stop:step]`\n",
    "- Reverse sequences with `[::-1]`\n",
    "- Slice strings in the same way as lists\n",
    "- Assign to slices to replace parts of a list\n",
    "- Use extended unpacking with `*` to capture remaining items\n",
    "- Unpack sequences into function arguments with `*` and `**`\n",
    "- Swap variables elegantly with unpacking\n",
    "\n",
    "## Congratulations\n",
    "\n",
    "You have completed all six tutorials in the Data Structures with Python series. You now have a solid foundation in lists, tuples, dictionaries, sets, comprehensions, slicing, and unpacking.\n",
    "\n",
    "To continue learning:\n",
    "\n",
    "- Explore the [Recipes](https://agilearn.co.uk/guides/data-structures/recipes/index) for practical, task-oriented recipes\n",
    "- Use the [Reference](https://agilearn.co.uk/guides/data-structures/reference/index) section for quick lookup of methods and operations\n",
    "- Read the [Concepts](https://agilearn.co.uk/guides/data-structures/concepts/index) section to deepen your understanding of concepts"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}