Python map, filter & reduce, explained
map, filter, and reduce are the classic functional trio for transforming, selecting,
and folding sequences. Python supports all three, but it also has comprehensions — so the
real interview question is usually when each is the right tool, not just how they work.
map — apply a function to every item
map(func, iterable) returns a lazy iterator that calls func on each element. Nothing
runs until you consume it.
nums = [1, 2, 3, 4]
squared = map(lambda x: x * x, nums) # a map object — nothing computed yet
list(squared) # [1, 4, 9, 16]
map can take multiple iterables, applying the function across them in parallel — handy and
concise:
list(map(lambda a, b: a + b, [1, 2, 3], [10, 20, 30])) # [11, 22, 33]
It stops at the shortest iterable. Because it's lazy, map over a huge sequence uses
constant memory.
filter — keep items that pass a test
filter(predicate, iterable) yields only the elements for which predicate returns truthy.
Passing None as the predicate keeps the truthy items.
nums = [0, 1, 2, 0, 3]
list(filter(lambda x: x > 1, nums)) # [2, 3]
list(filter(None, nums)) # [1, 2, 3] — drops falsy values
Like map, it returns a lazy iterator, so it composes cheaply into pipelines.
reduce — fold to a single value
reduce lives in functools (it was moved out of builtins in Python 3 because it's rarely
the clearest option). It repeatedly applies a two-argument function, carrying an accumulator.
from functools import reduce
reduce(lambda acc, x: acc + x, [1, 2, 3, 4], 0) # 10
reduce(lambda acc, x: acc * x, [1, 2, 3, 4], 1) # 24
The optional initial value (last argument) is also the result for an empty iterable, which
avoids a TypeError.
The Pythonic alternative: comprehensions
For map and filter, a list/generator comprehension is usually more readable — and it
avoids a lambda entirely.
nums = [1, 2, 3, 4]
# map + filter
[x * x for x in nums if x % 2 == 0] # [4, 16]
# vs the functional version
list(map(lambda x: x * x, filter(lambda x: x % 2 == 0, nums)))
The comprehension reads left-to-right and needs no lambda. Use a generator expression
(x*x for x in nums) when you want laziness like map gives.
When map/filter still win
map is genuinely nicer when you already have a named function — no lambda, no rebuilding:
names = [" alice ", "BOB "]
list(map(str.strip, names)) # ['alice', 'BOB '.strip()...] clean and direct
clean = [name.strip() for name in names] # comprehension equivalent
map(int, tokens) or map(str.upper, words) are crisp. The moment you need a lambda,
a comprehension is usually clearer.
reduce vs built-ins and loops
Most "reduce" tasks have a dedicated built-in: sum, min, max, any, all,
"".join. Reach for those first — they're faster and clearer. Save reduce for genuine
custom folds, and even then a plain for loop is often more readable to teammates.
# Don't:
reduce(lambda a, b: a + b, nums)
# Do:
sum(nums)
Recap
map applies a function to every item, filter keeps the ones passing a predicate, and
both return lazy iterators; reduce (in functools) folds a sequence to one value with
an accumulator and optional initial value. In modern Python, comprehensions and
generator expressions usually replace map/filter more readably — but map(func, ...)
with an existing named function stays elegant. For folds, prefer built-ins like sum,
max, and join over reduce whenever one exists.