Map, Filter and Reduce#
map, filter, and functools.reduce are the classic functional pipeline tools in Python: transform every element, keep those matching a predicate, and fold a sequence into one value. Python 3 returns lazy iterators from map and filter — understanding laziness is essential for performance and correctness.
How to use this page
Read with Higher Order Functions and Lambda Functions. For lazy traversal protocol details, see Custom Iterators. Modern style often prefers comprehensions — know both.
At a glance
| Track | Python Advanced → Functional Programming |
| Sections | 12 major topics |
| Outline | Use the right-hand TOC to jump |
Topics: The trio at a glance · map in depth · filter in depth · functools.reduce · Laziness and single consumption · Building pipelines · With named functions (cleaner than lambdas) · operator module — avoid trivial lambdas · … (+4 more)
- The trio at a glance
mapin depthfilterin depthfunctools.reduce- Laziness and single consumption
- Building pipelines
- With named functions (cleaner than lambdas)
operatormodule — avoid trivial lambdas- Side effects in
map— anti-pattern - Performance notes (Python 3.10+)
- Typing
- Real-world uses
The trio at a glance#
| Function | Purpose | Python 3 return type |
|---|---|---|
map(func, iterable) |
Transform each element | Iterator |
filter(pred, iterable) |
Keep elements where pred(x) is truthy |
Iterator |
reduce(func, iterable) |
Accumulate to single value | Scalar (any type) |
nums = [1, 2, 3, 4, 5]
squares = map(lambda x: x ** 2, nums)
evens = filter(lambda x: x % 2 == 0, nums)
list(squares) # [1, 4, 9, 16, 25]
list(evens) # [2, 4]
map in depth#
def to_str(x: int) -> str:
return str(x)
list(map(to_str, [1, 2, 3])) # ['1', '2', '3']
# multiple iterables — stops at shortest
list(map(lambda a, b: a + b, [1, 2, 3], [10, 20, 30]))
# [11, 22, 33]
list(map(lambda a, b: a + b, [1, 2], [10, 20, 30]))
# [11, 22] — truncated
map vs list comprehension#
# map
upper = map(str.upper, ["a", "b"])
# comprehension — usually more readable
upper = (s.upper() for s in ["a", "b"]) # lazy
upper = [s.upper() for s in ["a", "b"]] # eager
| Style | Prefer when |
|---|---|
| Comprehension | Pythonic transformation, conditionals |
map |
Function already named, functional chains, passing callable as arg |
Builtin map with None
map(None, a, b) pairs elements like zip(a, b) in Python 3 — rare; prefer zip.
filter in depth#
def is_positive(x: int) -> bool:
return x > 0
list(filter(is_positive, [-1, 0, 2, 3])) # [2, 3]
# pred=None keeps truthy values
list(filter(None, [0, "", "hi", [], [1]])) # ['hi', [1]]
filter vs comprehension#
Use filter when the predicate is a named function passed around; use comprehension for inline conditions.
functools.reduce#
from functools import reduce
reduce(lambda acc, x: acc + x, [1, 2, 3, 4]) # 10
# with initial accumulator
reduce(lambda acc, x: acc + x, [1, 2, 3], 100) # 106
# empty iterable — needs initial value
reduce(lambda acc, x: acc + x, [], 0) # 0
# reduce(lambda acc, x: acc + x, []) # TypeError
| Case | Behavior |
|---|---|
| Non-empty, no init | First element becomes initial acc |
| Empty, no init | TypeError |
| Empty, with init | Returns init |
Interview trap — empty reduce
Always provide an initial value when the iterable may be empty and you need a defined result.
Laziness and single consumption#
Chained lazy iterators defer work until consumption — good for memory, tricky when reused.
pipeline = map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, range(10)))
sum(pipeline) # 120 — computed on the fly
Building pipelines#
Classic functional chain#
from functools import reduce
nums = range(1, 11)
result = reduce(
lambda acc, x: acc + x,
map(
lambda x: x ** 2,
filter(lambda x: x % 2 == 0, nums),
),
)
# 220 — sum of squares of evens 2..10
Comprehension equivalent (often clearer)#
itertools alternatives#
import itertools
result = sum(
x ** 2
for x in itertools.filterfalse(lambda x: x % 2, nums) # odd filter inverse
)
With named functions (cleaner than lambdas)#
def square(x: int) -> int:
return x * x
def is_even(x: int) -> bool:
return x % 2 == 0
def add(acc: int, x: int) -> int:
return acc + x
from functools import reduce, partial
sum_even_squares = lambda data: reduce(
add,
map(square, filter(is_even, data)),
)
Partial Functions fix common arguments in pipelines.
operator module — avoid trivial lambdas#
from functools import reduce
import operator
reduce(operator.add, [1, 2, 3, 4]) # 10
list(map(operator.neg, [1, -2, 3])) # [-1, 2, -3]
# itemgetter for sorting keys — related pattern
from operator import itemgetter
sorted(users, key=itemgetter("name"))
Side effects in map — anti-pattern#
# BAD — map for side effects
list(map(print, items)) # works but idiomatically wrong
# GOOD
for item in items:
print(item)
map is for transformation, not imperative loops. Linters flag map(print, ...).
Performance notes (Python 3.10+)#
| Pattern | Relative speed | Notes |
|---|---|---|
| List comprehension | Fastest for eager lists | C-optimized |
map / filter |
Similar, iterator overhead | Better if not fully consumed |
reduce in Python |
Slower than sum, math.prod |
Use builtins when available |
| Generator pipeline | Best memory | Worst if you need random access |
# prefer builtin aggregations
sum(x ** 2 for x in nums if x % 2 == 0)
math.prod(nums)
max(nums)
any(pred(x) for x in nums)
Typing#
from typing import Iterator
from functools import reduce
def pipeline(data: list[int]) -> int:
squared: Iterator[int] = map(lambda x: x ** 2, data)
return reduce(int.__add__, squared, 0)
Modern code often uses from collections.abc import Iterable and explicit type vars in higher-order helpers.
Real-world uses#
| Domain | Pattern |
|---|---|
| ETL | map normalize fields → filter invalid rows → reduce merge |
| Config | filter(None, ...) strip falsy entries |
| Parsing | map(int, line.split()) |
| DSA | reduce for associative folds (with care) |
Interview traps (quick reference)#
| Trap | What goes wrong | Safe approach |
|---|---|---|
map result is a list in Py3 |
Type confusion | list(map(...)) if you need list |
Empty reduce without init |
TypeError |
Provide initializer |
| Reusing spent iterator | Missing elements | Materialize or recreate |
filter forgot truthiness |
Keeps wrong items | Remember None means "truthy" |
map for side effects |
Unpythonic, hard to read | for loop |
Mental model checklist#
- What return type do
mapandfilterproduce in Python 3? - When must
reducehave an initial accumulator? - How do you express
map+filter+ sum as one comprehension? - Why is
map(print, xs)discouraged? - When is
reducestill the right tool oversum/any/all?
What's next#
| Topic | Page |
|---|---|
| Higher-order functions | Higher Order Functions |
| Lambda | Lambda Functions |
| Partial | Partial Functions |
| Custom iterators | Custom Iterators |
| Closures in HOFs | Closures |