Python map(), filter(), reduce(), and Higher-Order Functions
Picture an assembly line in a factory. Raw materials enter at one end. At the first station, each piece is shaped. At the next station, defective pieces are removed. At the final station, all the good pieces are combined into one finished product.
Python has three functions that work exactly like this assembly line: map() transforms every item, filter() keeps only the items you want, and reduce() combines everything into a single result. Together, they let you process collections of data in a clean, readable way.
In this tutorial, you'll learn how these tools work, when to use them, and when a list comprehension might be a better choice. By the end, you'll be writing data pipelines that would make a factory foreman proud.
What Are Higher-Order Functions?
A higher-order function is a function that takes another function as an argument, or returns a function as its result. That's it.
You've already used one without realizing it. The built-in sorted() function has a key parameter where you pass in a function:
Here, len is passed as an argument to sorted(). The sorted function calls len on each item to decide the order. You didn't call len() yourself — you just told sorted which function to use.
map(), filter(), and reduce() all work this way. You give them a function and a collection, and they apply that function to the items. That's what makes them higher-order functions.
map() — Transform Every Item
map() takes a function and an iterable (like a list), applies the function to every item, and returns the transformed results. Think of it as the "shaping station" on our assembly line.
The syntax is: map(function, iterable). It returns a map object, which you usually convert to a list.
map(double, numbers) calls double(1), double(2), double(3), and so on. It collects all the results. The list() wrapper converts the map object into a regular list so we can print it.
Since writing a whole function just to double a number feels heavy, map() is often paired with a lambda — a tiny anonymous function:
You can pass any function to map() — your own functions, lambdas, or built-ins like str.upper, round, len, or int.
filter() — Keep Only What You Need
filter() takes a function and an iterable. The function must return True or False for each item. Only items where the function returns True are kept. Think of it as quality control on the assembly line — defective pieces are removed.
filter(is_even, numbers) calls is_even(1) which returns False (so 1 is dropped), is_even(2) returns True (so 2 is kept), and so on. Only the items that pass the test survive.
Passing bool to filter() is a neat trick. Empty strings, zero, None, and empty lists are all "falsy" in Python — bool() returns False for them. So filter(bool, items) removes all falsy values.
Combining map() and filter()
The real power comes from chaining these together. Filter first to select items, then map to transform them — just like stations on an assembly line.
Since both filter() and map() are lazy, you can chain them without creating intermediate lists. The data flows through the pipeline efficiently.
Here is a more practical example — processing a list of product prices:
reduce() — Combine All Items Into One
reduce() takes a function and an iterable, then combines all items into a single value. It's the final station on the assembly line — all the pieces get assembled into one product.
Unlike map() and filter(), reduce() is not a built-in. You need to import it from the functools module.
Here's how reduce() works step by step for the sum. The function takes two arguments: the accumulated result so far (a) and the next item (b):
a=1, b=2 gives 3a=3, b=3 gives 6a=6, b=4 gives 10a=10, b=5 gives 15map/filter vs List Comprehensions
Python offers another way to transform and filter lists: list comprehensions. They often produce more readable code than map() and filter(). Let's compare.
numbers = [1, 2, 3, 4, 5, 6]
# Double even numbers
evens = filter(lambda x: x % 2 == 0, numbers)
result = list(map(lambda x: x * 2, evens))
print(result) # [4, 8, 12]numbers = [1, 2, 3, 4, 5, 6]
# Double even numbers
result = [x * 2 for x in numbers if x % 2 == 0]
print(result) # [4, 8, 12]The list comprehension does both filtering and mapping in a single, readable line. For simple transformations, comprehensions are usually the better choice in Python.
So when should you use map() and filter()? They're useful when you already have a named function to apply:
Practice Exercises
Time to practice. These exercises start with individual functions and build up to combining them in pipelines. Remember to wrap map() and filter() in list() when printing.
Use map() with a lambda to square every number in the list [1, 2, 3, 4, 5].
Print the result as a list.
Expected output:
[1, 4, 9, 16, 25]Use filter() with a lambda to keep only words that have more than 3 characters.
Given: ["hi", "hello", "hey", "world", "ok", "python"]
Print the result as a list.
Expected output:
['hello', 'world', 'python']Read this code and predict the output. Then write a print() statement that produces the exact same output.
numbers = [10, 25, 30, 45, 50]
result = list(map(lambda x: x // 10, filter(lambda x: x >= 30, numbers)))
print(result)Think step by step: what does filter keep, then what does map do to each item?
This code should convert a list of temperature strings like ["32F", "100F", "212F"] into Celsius integers. But it has two bugs.
Expected output:
[0, 37, 100]Formula: Celsius = (Fahrenheit - 32) * 5 / 9, then round to the nearest integer.
Use reduce() from the functools module to calculate the product of all numbers in the list [2, 3, 4, 5].
The product is 2 * 3 * 4 * 5 = 120.
Print the result.
Expected output:
120Given a list of student scores, build a pipeline that:
1. Filters out failing scores (keep only scores >= 60)
2. Curves each passing score by adding 10 (use map)
3. Caps scores at 100 (use map with min)
Given: [45, 82, 55, 91, 67, 38, 73, 95]
Print the final list.
Expected output:
[92, 100, 77, 83, 100]Hint: to cap at 100, use min(score, 100).
Refactor this map/filter pipeline into a single list comprehension that produces the same output.
Original code:
words = ["Hello", "WORLD", "Python", "CODE", "test"]
result = list(map(str.lower, filter(lambda w: len(w) > 4, words)))Write a list comprehension that produces the same result and print it.
Expected output:
['hello', 'world', 'python']Summary
| Here's what you've learned about functional tools in Python: | ||
|---|---|---|
| --- | --- | --- |
map(fn, iterable) | Transform every item | Map object (lazy) |
filter(fn, iterable) | Keep items where fn returns True | Filter object (lazy) |
reduce(fn, iterable) | Combine all items into one value | Single value |
| Higher-order function | A function that takes or returns a function | Depends on the function |
The practical takeaway: use list comprehensions for most transform/filter tasks in Python. Reach for map() and filter() when you have a named function to apply or need lazy evaluation. Use reduce() sparingly — Python's built-ins cover most cases better.
What's Next?
You now have a strong foundation in Python functions. Next, explore [Error Handling](/python/python-try-except) to learn how to write code that handles mistakes gracefully instead of crashing.