When should you use lambda, map, filter, and reduce — and when should you avoid them?
lambda, map, and filter are concise for simple one-liners passed to higher-order functions, but list/generator comprehensions are usually more readable. reduce belongs in functools and is best reserved for cases where the fold operation is non-trivial; an explicit loop is often clearer.
How to think about it
This is partly a taste question. The interviewer wants to see you know exactly what each tool does and can use it correctly — and that you know when a comprehension reads better. Leaning on lambda/map/filter everywhere is a code smell; never using them where they’d be clean is the opposite miss.
Take them one at a time.
lambda is an anonymous, single-expression function. Its real job is to be passed inline to another function:
square = lambda x: x ** 2 # works, but just use def
sorted_pairs = sorted(pairs, key=lambda p: p[1]) # this is the idiomatic use
map applies a function to every element and returns a lazy iterator:
labels = list(map(lambda s: "pass" if s >= 0.5 else "fail", scores))
# usually clearer as a comprehension:
labels = ["pass" if s >= 0.5 else "fail" for s in scores]
filter keeps the elements where the predicate is truthy — also lazy:
evens = list(filter(lambda x: x % 2 == 0, range(10)))
# comprehension equivalent:
evens = [x for x in range(10) if x % 2 == 0]
reduce folds a whole sequence into one value. It lives in functools — Guido moved it out of the builtins on purpose, because an explicit loop is usually clearer:
from functools import reduce
product = reduce(lambda acc, x: acc * x, [1, 2, 3, 4, 5]) # 120
# but for this specific case:
import math
product = math.prod([1, 2, 3, 4, 5])
A worked example
from functools import reduce
import math
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# lambda as a sort key — the cleanest, most idiomatic use
words = ["banana", "fig", "apple", "cherry"]
print("Sorted by length:", sorted(words, key=lambda w: len(w)))
# map returns a lazy iterator — list() materialises it
print("Squares via map:", list(map(lambda x: x**2, nums)))
print("Squares via comp:", [x**2 for x in nums]) # usually preferred
# filter keeps the truthy ones
print("Evens via filter:", list(filter(lambda x: x % 2 == 0, nums)))
# map with an ALREADY-named function is where map shines
print("Strings via map(str):", list(map(str, [1, 2, 3])))
# reduce folds to a single value
print("Product via reduce:", reduce(lambda acc, x: acc * x, nums))
print("Same with math.prod:", math.prod(nums))
Sorted by length: ['fig', 'apple', 'banana', 'cherry']
Squares via map: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Squares via comp: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Evens via filter: [2, 4, 6, 8, 10]
Strings via map(str): ['1', '2', '3']
Product via reduce: 3628800
Same with math.prod: 3628800
The two “squares” lines produce identical results — which is exactly why the comprehension usually wins: same output, less ceremony, no lambda and no list() wrapper. map earns its keep mainly when the function is already named, as in map(str, nums).
Where each one shines
lambdaas thekeytosorted,max, ormin— clean and conventional; keep it.mapwith a named function:map(str, nums)reads a touch better than the comprehension.filter(None, values)to drop every falsy element in one phrase.reducewhen a left-fold is the clearest statement of intent — composing functions, folding a tree.
For anything past a single expression, a comprehension or a plain loop wins on readability every time.