Advanced Python Techniques
map(), filter(), and reduce(): Functional Tools for Iterables
5 min read
Functions that take a function
map(), filter(), and reduce() all share one idea: instead of writing a for loop yourself, you hand Python a function and an iterable, and let Python do the looping. This style — passing functions around as arguments — is called functional programming, and it pairs naturally with the lambda functions you just learned, since a lambda is often exactly the small, throwaway function these tools expect.
Diagram — map(), filter(), and reduce() Side by Side
map(): transform every item
map(function, iterable) applies function to every item in iterable and hands back a new sequence of the results — same number of items in as out, each one transformed.
output = list(map(lambda x: x+3, [1, 2, 3, 4]))
print(output)
Output:
[4, 5, 6, 7]
Notice the list(...) wrapped around map(...). In Python 3, map() doesn't return a list directly — it returns a map object, a lazy iterator that computes each result only as it's asked for. That's efficient for huge inputs (nothing is computed until you actually need it), but it means you have to convert it — with list(), or by looping over it — before you can print it or index into it.
map() isn't limited to a lambda. Any function that takes one argument works:
def square(x):
return x**2
print(list(map(square, [1, 2, 3, 4])))
Output:
[1, 4, 9, 16]
filter(): keep only what passes a test
filter(function, iterable) calls function on every item and keeps only the ones where it returns a truthy value — the function acts as a yes/no test, not a transformation.
age = [5, 11, 16, 19, 24, 42]
adults = filter(lambda x: x > 18, age)
print(list(adults))
Output:
[19, 24, 42]
Just like map(), filter() returns a lazy filter object in Python 3, so it needs list() to display or index. The items that come out are unchanged — filter() never modifies a value, it only decides whether each one stays or goes.
reduce(): roll everything into one value
reduce() is different from map() and filter() in one important way: it isn't a built-in function. You have to import it from the functools module. It takes a function of two arguments and uses it to combine the items of an iterable, two at a time, until only one value is left.
from functools import reduce
def sum(x, y):
return x+y
L = [10, 20, 30]
result = reduce(sum, L)
print(result)
Output:
60
Trace through what happens: reduce() first calls sum(10, 20) to get 30, then calls sum(30, 30) — the running total combined with the next item — to get 60. That "running total" pattern is what reduce() is for: turning a list into a single number, string, or other combined value.
The combining function is usually written as a lambda rather than a named function, since (like with map() and filter()) it's typically only needed for this one call:
from functools import reduce
L = [10, 20, 30]
result = reduce(lambda x, y: x+y, L)
print(result)
Output:
60
For the specific case of summing or finding the max/min of a list, Python's built-in sum(), max(), and min() are simpler and faster than reaching for reduce() — reduce() earns its place when the combining logic is something more custom than plain addition.
The Pythonic alternative: list comprehensions
You met list comprehensions back in List Comprehensions and zip(), and it's worth naming directly: most map() and filter() calls can be rewritten as a comprehension, and in the Python community, the comprehension is usually considered more readable.
nums = [1, 2, 3, 4]
# map() + lambda
doubled = list(map(lambda x: x+3, nums))
# equivalent list comprehension
doubled = [x+3 for x in nums]
age = [5, 11, 16, 19, 24, 42]
# filter() + lambda
adults = list(filter(lambda x: x > 18, age))
# equivalent list comprehension
adults = [x for x in age if x > 18]
Both versions produce identical results. The comprehension reads as one continuous English-like sentence, while map()/filter() require mentally unwrapping a function call around a lambda around an iterable — which is why most style guides, including the community-maintained PEP 8, favor comprehensions when the transformation is simple. map() and filter() still earn their keep when you're passing an existing named function (no lambda needed), or when working with very large or infinite iterables where laziness matters.
That said, a comprehension can itself live inside a lambda when you need the whole thing to be a single anonymous expression — useful for flattening a list of lists in one line:
flatten = lambda l: [item for sublist in l for item in sublist]
L = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(flatten(L))
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
This is the nested-for comprehension from lesson 17, wrapped in a lambda so it can be assigned and called like any other function — flatten here is doing the same "loop over the outer list, then loop over each inner list" work you'd otherwise write as two nested for loops.
Key takeaway
map(function, iterable) transforms every item and returns the same number of results; filter(function, iterable) tests every item and keeps only the ones that pass; reduce(function, iterable) — imported from functools — collapses everything into a single accumulated value. All three commonly pair with a lambda as their function argument, but for straightforward transformations and filters, a list comprehension is usually the more readable choice.
What's next?
You've now covered Python's functional toolbox for working with collections. Next, a shift in direction: Classes and Objects introduces object-oriented programming — how to define your own data types, bundling data and behavior together instead of passing values through functions.