{"id":"functional-filter","slug":"filter","title":"Selecting with filter","text":"`map` changes every item. **`filter`** keeps some of the items as they are and drops the rest. You write it as `filter(function, series)`: it calls the function on each item and keeps the items for which the function returns `True`:\n\n```python\nnumbers = [8, 3, 15, 4, 23, 42]\nfor number in filter(lambda n: n > 10, numbers):\n    print(number)\n```\n\n```output\n15\n23\n42\n```\n\nA named function works just as well, and a longer condition often reads better with a name:\n\n```python\ndef is_palindrome(word):\n    return word == word[::-1]\n\n\nwords = [\"level\", \"python\", \"radar\", \"map\", \"noon\"]\nprint(list(filter(is_palindrome, words)))\n```\n\n```output\n['level', 'radar', 'noon']\n```\n\nThat's the same as the comprehension `[word for word in words if is_palindrome(word)]`.\n\n### filter gives an iterator too\n\nLike `map`, `filter` returns an iterator, which is used up after one pass. That can bite when you use the result twice, for example to count the items and then to print them:\n\n```python\nclass Task:\n    def __init__(self, name, done):\n        self.name = name\n        self.done = done\n\n\ntasks = [Task(\"shop\", True), Task(\"cook\", False), Task(\"call Grandma\", False)]\nopen_tasks = filter(lambda task: not task.done, tasks)\nprint(\"Tasks left:\", len(list(open_tasks)))\nfor task in open_tasks:\n    print(task.name)\n```\n\n```output\nTasks left: 2\n```\n\nThe loop printed nothing: `list(open_tasks)` already took every item, so nothing was left for the loop. Store the list once and use it as often as you like:\n\n```python\nclass Task:\n    def __init__(self, name, done):\n        self.name = name\n        self.done = done\n\n\ntasks = [Task(\"shop\", True), Task(\"cook\", False), Task(\"call Grandma\", False)]\nopen_tasks = list(filter(lambda task: not task.done, tasks))\nprint(\"Tasks left:\", len(open_tasks))\nfor name in map(lambda task: task.name.upper(), open_tasks):\n    print(name)\n```\n\n```output\nTasks left: 2\nCOOK\nCALL GRANDMA\n```\n\n### filter and map together\n\nThe second program also used `map` on what `filter` kept. Both take a series and give a series, so one's result can go straight into the other. Such a chain is read from the inside out:\n\n```python\nnames = list(map(lambda task: task.name, filter(lambda task: not task.done, tasks)))\n```\n\nFirst `filter` keeps the open tasks, then `map` takes their names. The comprehension `[task.name for task in tasks if not task.done]` does the same in one go.\n","task":"Fill in the blank with the condition, so that `filter` keeps only the numbers that are divisible by 3. The program should print:\n\n```\n[9, 21, 3, 30]\n```\n","kind":"fill","seed":"numbers = [9, 14, 21, 3, 8, 30]\nprint(list(filter(lambda n: [[n % 3 == 0]], numbers)))\n","solution":"numbers = [9, 14, 21, 3, 8, 30]\nprint(list(filter(lambda n: n % 3 == 0, numbers)))\n","hints":["The lambda gets one number and must return `True` for the numbers to keep.","A number is divisible by 3 when the remainder is zero: `n % 3 == 0`."],"tests":[{"name":"Output","stdin":"","expect":"[9, 21, 3, 30]","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"numbers = [5, 12, 7, 20, 3]\nbig = filter(lambda n: n > 6, numbers)\nprint(next(big))\nprint(list(big))\nprint(list(map(lambda n: n * 2, filter(lambda n: n < 6, numbers))))\n","solution":"numbers = [5, 12, 7, 20, 3]\nbig = filter(lambda n: n > 6, numbers)\nprint(next(big))\nprint(list(big))\nprint(list(map(lambda n: n * 2, filter(lambda n: n < 6, numbers))))\n","hints":["`filter` gives an iterator, so `next` works on it, just as on a generator. The first number over 6 is 12.","`list(big)` takes the numbers over 6 that are left after that.","On the last line, read from the inside out. `filter` keeps 5 and 3, then `map` doubles them."],"tests":[{"name":"Output","stdin":"","expect":"12\n[7, 20]\n[10, 6]","hidden":false}],"require":[],"forbid":[],"lines":["12","[7, 20]","[10, 6]"]},{"task":"The class `Book` holds a book's title and whether it's on loan. Write a function `available_titles(books)` that returns a list of the titles of the books that are **not** on loan, in alphabetical order. Use `filter` and `map`. For example,\n\n```python\nbooks = [Book(\"Matilda\", False), Book(\"Dracula\", True), Book(\"Beloved\", False)]\nprint(available_titles(books))\n```\n\nshould print:\n\n```\n['Beloved', 'Matilda']\n```\n","kind":"code","seed":"class Book:\n    def __init__(self, title, on_loan):\n        self.title = title\n        self.on_loan = on_loan\n\n\ndef available_titles(books):\n    pass\n\n\nif __name__ == \"__main__\":\n    books = [Book(\"Matilda\", False), Book(\"Dracula\", True), Book(\"Beloved\", False)]\n    print(available_titles(books))\n","solution":"class Book:\n    def __init__(self, title, on_loan):\n        self.title = title\n        self.on_loan = on_loan\n\n\ndef available_titles(books):\n    available = filter(lambda book: not book.on_loan, books)\n    titles = map(lambda book: book.title, available)\n    return sorted(titles)\n\n\nif __name__ == \"__main__\":\n    books = [Book(\"Matilda\", False), Book(\"Dracula\", True), Book(\"Beloved\", False)]\n    print(available_titles(books))\n","hints":["First `filter` the books whose `on_loan` is `False`: the lambda can return `not book.on_loan`.","Then `map` each of those books to its title.","`sorted` accepts an iterator and returns a sorted list, which is just what the function should return."],"tests":[{"name":"Test 1","stdin":"","call":"books = [Book(\"Matilda\", False), Book(\"Dracula\", True), Book(\"Beloved\", False)]\nprint(available_titles(books))\n","expect":"['Beloved', 'Matilda']","hidden":false},{"name":"available_titles([Book(\"Ulysses\", True), Book(\"Emma\", False), Book(\"Carrie\", False), Book(\"Anna\", False)])","stdin":"","call":"available_titles([Book(\"Ulysses\", True), Book(\"Emma\", False), Book(\"Carrie\", False), Book(\"Anna\", False)])\n","expect":"['Anna', 'Carrie', 'Emma']","hidden":true,"returns":true},{"name":"available_titles([Book(\"Ulysses\", True)])","stdin":"","call":"available_titles([Book(\"Ulysses\", True)])\n","expect":"[]","hidden":true,"returns":true}],"require":[{"pattern":"\\bfilter\\s*\\(","message":"Pick the books with `filter`."},{"pattern":"\\bmap\\s*\\(","message":"Take the titles with `map`."}],"forbid":[]}]}