{"id":"functional-map","slug":"map","title":"Transforming with map","text":"Most of your programs so far have worked by changing things, one step at a time: a loop adds to a total, `append` makes a list longer, a method changes an attribute of an object. **Functional programming** is a way of writing programs that works the other way round. It computes new values from old ones with functions, and it leaves the old values as they are. A way of writing programs like this is called a **programming paradigm**. Here's one small job done both ways:\n\n```python\nnumbers = [3, 8, 5]\n\ndoubled = []\nfor number in numbers:\n    doubled.append(number * 2)\nprint(doubled)\n\nprint([number * 2 for number in numbers])\n```\n\n```output\n[6, 16, 10]\n[6, 16, 10]\n```\n\nThe loop changes the list `doubled` in every round, so to know what it holds at some point, you follow the rounds. The comprehension is one expression that says what the new list is, and nothing changes on the way. That's the functional way of thinking, and you've used it since comprehensions. This step and the next two show three classic functional tools: `map`, `filter` and `reduce`. Each of them takes a function as an argument, just as `sorted` takes a key.\n\n### map\n\nThe function **`map`** calls a function on every item of a series and gives the results. You write it as `map(function, series)`:\n\n```python\ntexts = [\"12\", \"-4\", \"30\"]\nnumbers = map(int, texts)\nfor number in numbers:\n    print(number * 2)\n```\n\n```output\n24\n-8\n60\n```\n\nThe function can be any function that takes one argument: one of Python's own, one you wrote, or a lambda:\n\n```python\ndef initial(name):\n    return name[0] + \".\"\n\n\nnames = [\"Ada\", \"Linus\", \"Grace\"]\nprint(list(map(initial, names)))\nprint(list(map(lambda name: name.upper(), names)))\n```\n\n```output\n['A.', 'L.', 'G.']\n['ADA', 'LINUS', 'GRACE']\n```\n\nA list comprehension does the same: `[initial(name) for name in names]`. Neither is more correct than the other, so choose whichever reads better to you.\n\n### map gives an iterator\n\n`map` doesn't return a list. It returns a `map` object, an **iterator**: like a generator, it computes each value only when it's asked for, and it's used up after one pass. Printing it shows only `<map object at 0x7f3a2c>`, which is why the example above wraps it in `list()`. Going through the same map twice gives nothing the second time:\n\n```python\nprices = [2.0, 3.5, 10.0]\nwith_tax = map(lambda price: price * 1.25, prices)\n\nfor price in with_tax:\n    print(price)\nprint(\"Once more:\")\nfor price in with_tax:\n    print(price)\n```\n\n```output\n2.5\n4.375\n12.5\nOnce more:\n```\n\nWhen you need the values more than once, store a list instead: `with_tax = list(map(...))`.\n\n### map and your own objects\n\n`map` works with any items, objects of your own classes too. The function gets one object and can compute anything from it, such as a number or a line of text made from its attributes:\n\n```python\nclass Room:\n    def __init__(self, name, width, length):\n        self.name = name\n        self.width = width\n        self.length = length\n\n\nrooms = [Room(\"kitchen\", 3, 4), Room(\"hall\", 1.5, 6), Room(\"bedroom\", 4, 3.5)]\nprint(list(map(lambda room: room.width * room.length, rooms)))\nprint(list(map(lambda room: f\"{room.name}: {room.width} x {room.length} m\", rooms)))\n```\n\n```output\n[12, 9.0, 14.0]\n['kitchen: 3 x 4 m', 'hall: 1.5 x 6 m', 'bedroom: 4 x 3.5 m']\n```\n","task":"Fill in the blanks so that the program converts the temperatures to Fahrenheit with `map` and prints them as a list:\n\n```\n[32.0, 77.0, 212.0]\n```\n","kind":"fill","seed":"def to_fahrenheit(celsius):\n    return celsius * 9 / 5 + 32\n\n\ntemperatures = [0, 25, 100]\nprint([[list]]([[map]](to_fahrenheit, temperatures)))\n","solution":"def to_fahrenheit(celsius):\n    return celsius * 9 / 5 + 32\n\n\ntemperatures = [0, 25, 100]\nprint(list(map(to_fahrenheit, temperatures)))\n","hints":["The inner function calls `to_fahrenheit` on every temperature. The outer one turns the result into a list.","`map(to_fahrenheit, temperatures)` gives an iterator, and `list(...)` around it gives the list to print."],"tests":[{"name":"Output","stdin":"","expect":"[32.0, 77.0, 212.0]","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"words = [\"one\", \"two\", \"three\"]\nlengths = map(len, words)\nprint(list(lengths))\nprint(list(lengths))\nprint(list(map(lambda word: word[::-1], words)))\n","solution":"words = [\"one\", \"two\", \"three\"]\nlengths = map(len, words)\nprint(list(lengths))\nprint(list(lengths))\nprint(list(map(lambda word: word[::-1], words)))\n","hints":["`map(len, words)` gives the length of each word.","The first `list` uses up the iterator `lengths`, so the second one gets nothing.","`word[::-1]` is the word backwards."],"tests":[{"name":"Output","stdin":"","expect":"[3, 3, 5]\n[]\n['eno', 'owt', 'eerht']","hidden":false}],"require":[],"forbid":[],"lines":["[3, 3, 5]","[]","['eno', 'owt', 'eerht']"]},{"task":"The class `Product` holds a product's name and price. Write a function `price_tags(products)` that uses `map` to return a list of price tags, one string for each product: the name, a colon and a space, and the price with two decimals. For example,\n\n```python\nproducts = [Product(\"tea\", 3.5), Product(\"honey\", 7), Product(\"lemon\", 0.45)]\nprint(price_tags(products))\n```\n\nshould print:\n\n```\n['tea: 3.50', 'honey: 7.00', 'lemon: 0.45']\n```\n","kind":"code","seed":"class Product:\n    def __init__(self, name, price):\n        self.name = name\n        self.price = price\n\n\ndef price_tags(products):\n    pass\n\n\nif __name__ == \"__main__\":\n    products = [Product(\"tea\", 3.5), Product(\"honey\", 7), Product(\"lemon\", 0.45)]\n    print(price_tags(products))\n","solution":"class Product:\n    def __init__(self, name, price):\n        self.name = name\n        self.price = price\n\n\ndef price_tags(products):\n    return list(map(lambda product: f\"{product.name}: {product.price:.2f}\", products))\n\n\nif __name__ == \"__main__\":\n    products = [Product(\"tea\", 3.5), Product(\"honey\", 7), Product(\"lemon\", 0.45)]\n    print(price_tags(products))\n","hints":["The function given to `map` gets one `Product` and returns its tag. An f-string makes the tag: `f\"{product.name}: {product.price:.2f}\"`.","`map` gives an iterator, but the function should return a list: wrap it in `list(...)`."],"tests":[{"name":"Test 1","stdin":"","call":"products = [Product(\"tea\", 3.5), Product(\"honey\", 7), Product(\"lemon\", 0.45)]\nprint(price_tags(products))\n","expect":"['tea: 3.50', 'honey: 7.00', 'lemon: 0.45']","hidden":false},{"name":"price_tags([Product(\"bread\", 2.199), Product(\"jam\", 12)])","stdin":"","call":"price_tags([Product(\"bread\", 2.199), Product(\"jam\", 12)])\n","expect":"['bread: 2.20', 'jam: 12.00']","hidden":true,"returns":true},{"name":"price_tags([])","stdin":"","call":"price_tags([])\n","expect":"[]","hidden":true,"returns":true}],"require":[{"pattern":"\\bmap\\s*\\(","message":"Make the tags with `map`."}],"forbid":[]}]}