Pythonmap, filter & reduce

map, filter & reduce

map, filter, and reduce are three functional-programming tools for working with iterables without writing an explicit loop. Each takes a function and applies it across a sequence, but they differ in what they produce: map transforms every element, filter selects a subset of elements, and reduce collapses the whole sequence down to a single value.

map: transform every element

map(func, iterable) applies func to every item in iterable and produces one output per input. It does not return a list — it returns a lazy map object, which is an iterator that computes each result only as it's requested. To see the results (or use them like a list), wrap the call in list(...).

Python
numbers = [1, 2, 3, 4, 5]

doubled = map(lambda n: n * 2, numbers)
print(doubled)          # a map object, not a list
print(list(doubled))    # materializes the results
<map object at 0x0000023F5B2A1A90>
[2, 4, 6, 8, 10]
Note
Because map objects are lazy iterators, they can only be consumed once. Iterating over doubled a second time above would yield nothing — you'd need to call map(...) again to get a fresh iterator.
filter: select elements

filter(func, iterable) keeps only the items for which func returns a truthy value, dropping the rest. Like map, it's lazy — it returns a filter object, and you need list(...) (or a for loop, or another consumer) to actually pull the values out.

Python
numbers = [1, 2, 3, 4, 5, 6, 7, 8]

evens = filter(lambda n: n % 2 == 0, numbers)
print(evens)          # a filter object, not a list
print(list(evens))    # materializes the results
<filter object at 0x0000023F5B2A1B20>
[2, 4, 6, 8]
reduce: accumulate to a single value

reduce(func, iterable, initial) lives in the functools module rather than being a builtin. Unlike map and filter, it does not return an iterator at all — it eagerly walks the whole iterable and returns one final accumulated value. func must take two arguments: the running accumulator and the next item.

Python
from functools import reduce

numbers = [1, 2, 3, 4, 5]

total = reduce(lambda acc, n: acc + n, numbers, 0)
print(total)

product = reduce(lambda acc, n: acc * n, numbers, 1)
print(product)
15
120

The third argument, initial, seeds the accumulator before the first call to func. If you omit it, reduce uses the iterable's first element as the starting accumulator and begins combining from the second element onward. That works fine for a non-empty list, but calling reduce on an empty iterable with no initial raises a TypeError — passing initial explicitly avoids that edge case.

Python
from functools import reduce

# without an initial value: the first element seeds the accumulator
print(reduce(lambda acc, n: acc + n, [1, 2, 3, 4, 5]))

# empty iterable + no initial value raises TypeError
reduce(lambda acc, n: acc + n, [])
15
Traceback (most recent call last):
  ...
TypeError: reduce() of empty iterable with no initial value
Warning
Always pass an explicit initial value to reduce when the iterable might be empty at runtime — otherwise your program crashes instead of returning a sensible default like 0.
Laziness: the key difference

It's worth being precise about this, since the three functions are often lumped together: map and filter both return lazy iterator objects — nothing is computed until you iterate over them or wrap them in list(). reduce, on the other hand, does not return an iterator at all; it immediately walks the entire iterable and hands back one final value. There's no lazy "reduce object" to speak of.

map/filter vs. list comprehensions

Both map and filter have direct list comprehension equivalents, and most Python style guides favor the comprehension form for readability.

Goal

map / filter

List comprehension

Double every number

list(map(lambda n: n * 2, numbers))

[n * 2 for n in numbers]

Keep only even numbers

list(filter(lambda n: n % 2 == 0, numbers))

[n for n in numbers if n % 2 == 0]

Double the even numbers

list(map(lambda n: n * 2, filter(lambda n: n % 2 == 0, numbers)))

[n * 2 for n in numbers if n % 2 == 0]

Note
Comprehensions are generally considered more Pythonic than chaining map and filter, especially once you combine a transform and a condition — the comprehension form reads top-to-bottom as one expression instead of nesting function calls. reduce has no direct comprehension equivalent, though, since it isn't transforming or filtering elements — it's accumulating them into a single result, which is a fundamentally different operation.
Tip
map and filter still shine when you already have a named function (rather than a throwaway lambda) to pass in — list(map(str.upper, words)) is arguably cleaner than [str.upper(w) for w in words].
Summary
  • map(func, iterable) transforms every element and returns a lazy iterator — wrap it in list() to see the results

  • filter(func, iterable) keeps only elements where func returns truthy, also returning a lazy iterator

  • reduce(func, iterable, initial) from functools eagerly folds the iterable down to one value, not an iterator

  • Omitting reduce's initial value uses the first element as the seed, and raises TypeError on an empty iterable

  • List comprehensions are usually the more readable choice for map/filter-style work; reduce has no comprehension equivalent