Skip to content

map, filter & reduce Interview Questions & Answers

16 questions Updated 2026-06-18 Share:

Python interview questions on map laziness and multiple iterables, filter, functools.reduce, map/filter vs comprehensions, and consuming lazy iterators only once.

Read the in-depth guidePython map, filter & reduce Explained — Functional Transforms vs Comprehensions(opens in new tab)
16 of 16

map(func, iterable) applies func to each item, returning a lazy iterator in Python 3 — nothing is computed until you iterate it (or wrap it in list()). This saves memory on large inputs because results are produced one at a time.

nums = [1, 2, 3]
result = map(lambda x: x * 2, nums)
print(result)          # <map object ...> — not evaluated yet
print(list(result))    # [2, 4, 6] — now it runs

Because it's lazy, map never builds the full result in memory unless you ask for it. In Python 2 map returned a list — a common gotcha when porting code.

Pass several iterables and map calls func with one item from each, in parallel — func must accept that many arguments. It stops at the shortest iterable, so mismatched lengths simply truncate.

a = [1, 2, 3]
b = [10, 20, 30]
list(map(lambda x, y: x + y, a, b))   # [11, 22, 33]

list(map(pow, [2, 3, 4], [10, 11]))   # [1024, 177147] — stops at 2 items

This is handy for element-wise combining without an explicit zip. If you need the longest length (padding the gaps), use itertools.zip_longest instead.

filter(predicate, iterable) keeps only the items for which predicate returns truthy — also a lazy iterator in Python 3. As a special case, passing None as the predicate keeps the items that are truthy themselves.

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

Use it to drop elements by a condition. For complex conditions, a comprehension with if is usually more readable than filter(lambda ...).

functools.reduce(func, iterable, initial) folds an iterable into a single value by repeatedly applying a two-argument function, carrying an accumulator. In Python 3 it was moved out of the builtins into functools — Guido considered it less readable than an explicit loop, so it was demoted to discourage casual use.

from functools import reduce

reduce(lambda acc, x: acc + x, [1, 2, 3, 4], 0)   # 10
# step by step: 0+1=1, 1+2=3, 3+3=6, 6+4=10

For common reductions, prefer the dedicated built-ins — sum, max, min, math.prod, any, all — which are clearer and faster. Reach for reduce only when no built-in expresses the fold.

A list/generator comprehension is the more Pythonic choice when you'd otherwise write a lambda, because it's more readable and avoids the function-call overhead per item. map/filter shine when you can pass an existing named function (no lambda) and want a lazy iterator with minimal syntax.

nums = [1, 2, 3, 4]

[x * 2 for x in nums if x % 2]     # comprehension — clearest with a condition
list(map(str, nums))               # map with a built-in — clean, no lambda
list(map(lambda x: x * 2, nums))   # lambda makes map less readable

Rule of thumb: if it needs a lambda, use a comprehension; if you're mapping an already-named function, map reads fine. For laziness on huge data, a generator expression (... for ...) gives both.

map/filter (and generators) are one-shot iterators: iterating them advances an internal position that is never reset. Once exhausted, they yield nothing on a second pass — a frequent source of "my second loop is empty" bugs.

doubled = map(lambda x: x * 2, [1, 2, 3])
list(doubled)    # [2, 4, 6]
list(doubled)    # [] — already consumed!

doubled = list(map(lambda x: x * 2, [1, 2, 3]))  # materialize once
sum(doubled); max(doubled)   # reuse freely

If you need to iterate more than once, convert to a list (or other concrete collection) up front. Use the lazy iterator directly only when a single pass is enough.

Functionally yes — both are lazy one-pass iterators. map is slightly faster when f is an existing function (no per-item Python-level call setup), but a generator expression wins for readability when you'd otherwise need a lambda. With a lambda, map offers no speed benefit.

map(str, nums)                    # clean: existing function
(x * 2 for x in nums)             # clearer than map(lambda x: x*2, nums)

Rule of thumb: use map with a named function; use a genexpr/comprehension when the transform is an expression or needs a lambda.

map(f, a, b) pulls one item from each iterable per call and stops at the shortest — like zip then starmap. The function receives one argument per iterable.

list(map(lambda x, y: x + y, [1, 2, 3], [10, 20]))   # [11, 22] -> stops short
# equivalent to:
from itertools import starmap
list(starmap(lambda x, y: x + y, zip([1,2,3], [10,20])))

Rule of thumb: multi-iterable map is "zip + apply", truncating to the shortest input.

Passing None as the function makes filter keep only truthy items — dropping 0, '', None, empty containers, False. It's a quick way to remove "empty" values.

list(filter(None, [0, 1, "", "a", None, [], [2]]))   # [1, 'a', [2]]

Rule of thumb: filter(None, xs) is shorthand for "keep the truthy ones".

A comprehension expresses map + filter together in one readable expression, avoids lambdas, and returns a concrete list/set/dict directly. map/filter chained with lambdas get nested and hard to read. Reserve map/filter for passing existing functions.

[x * 2 for x in nums if x > 0]                   # clear
list(map(lambda x: x*2, filter(lambda x: x>0, nums)))   # noisy

Rule of thumb: comprehension for transform-and-filter logic; map/filter only when handing off a named function.

They return one-shot iterators, not lists. They do no work until iterated, can be fully consumed once, and have no len() or indexing. Forgetting this leads to "empty on the second pass" bugs and TypeError on len.

m = map(str, range(3))
list(m)            # ['0', '1', '2']
list(m)            # []  -> already exhausted
len(m)             # TypeError

Rule of thumb: wrap in list() if you need to reuse, index, or measure the result.

Because for most real uses a named built-in or a loop is clearer: sum, max, min, math.prod, any, all, "".join. reduce forces the reader to mentally execute a fold, which is error-prone. It now lives in functools to signal "use sparingly".

from functools import reduce
reduce(lambda a, b: a + b, nums)     # prefer sum(nums)

Rule of thumb: reach for a specific built-in; use reduce only for genuinely custom accumulations.

No. Because map is lazy, map(print, items) does nothing until consumed, and using it only for side effects is unidiomatic and confusing. Use a plain for loop when you want effects, not a transformed result.

map(send, messages)              # bug: nothing sent (never iterated)
for m in messages:               # correct
    send(m)

Rule of thumb: map/comprehensions are for producing values; use a for loop for side effects.

Chain lazy operations — generator expressions or map/filter — so data flows through one item at a time without building intermediate lists. This keeps memory flat even for huge or infinite sources.

lines = (l.strip() for l in open("big.log"))
errors = (l for l in lines if "ERROR" in l)
codes = map(parse_code, errors)        # nothing materialized yet
first10 = list(islice(codes, 10))      # only now does work happen

Rule of thumb: keep the pipeline lazy and materialize only at the end (or with islice) to process large data with constant memory.

When your iterable already contains argument tuples and you want them unpacked into the function. map would pass each tuple as a single argument; itertools.starmap calls f(*tuple).

from itertools import starmap
points = [(0, 0), (3, 4)]
list(starmap(math.hypot, points))      # [0.0, 5.0]

Rule of thumb: starmap when elements are pre-packed arg tuples; map for single-argument calls.

Feed (key, value) pairs into dict(). Combine zip to pair keys with values, or map to compute one side. A dict comprehension is the most readable for non-trivial logic.

keys, vals = ["a", "b"], [1, 2]
dict(zip(keys, vals))                       # {'a': 1, 'b': 2}
dict((k, len(k)) for k in words)            # via genexpr
{k: len(k) for k in words}                  # comprehension, clearest

Rule of thumb: dict(zip(...)) to align two sequences; a dict comprehension when the value needs computing.

More ways to practice

The self-quiz is live. Join our channel for updates, new content & tech tips.

Join our WhatsApp Channel