map and filter are two built-in functions that pop up everywhere in data processing. They take a function and an iterable, and produce a new iterable.
map — transform each item
map(func, iterable) applies func to every item and yields the result:
nums: list[int] = [1, 2, 3, 4, 5]
squared = map(lambda n: n * n, nums)
print(list(squared)) # [1, 4, 9, 16, 25]
The first argument is a function (often a lambda); the second is the iterable to walk through.
map returns a lazy iterator (Section 8), not a list. To see the values, wrap it in list() — or loop over it.
map with a named function
Lambdas are convenient, but map works with any function:
def to_celsius(fahrenheit: float) -> float:
return (fahrenheit - 32) * 5 / 9
temps_f: list[float] = [32.0, 68.0, 100.0]
temps_c: list[float] = list(map(to_celsius, temps_f))
print(temps_c) # [0.0, 20.0, 37.777...]
map with multiple iterables
map can also walk through several iterables in parallel:
xs: list[int] = [1, 2, 3]
ys: list[int] = [10, 20, 30]
sums = map(lambda a, b: a + b, xs, ys)
print(list(sums)) # [11, 22, 33]
The function gets one item from each iterable per call.
filter — keep items that pass a test
filter(func, iterable) keeps only items where func(item) returns True:
nums: list[int] = [1, 2, 3, 4, 5, 6, 7, 8]
evens = filter(lambda n: n % 2 == 0, nums)
print(list(evens)) # [2, 4, 6, 8]
Like map, filter returns a lazy iterator.
filter with None
If the function is None, filter keeps items that are truthy:
items: list[str | None] = ["python", None, "ai", "", "ml"]
non_empty = list(filter(None, items))
print(non_empty) # ['python', 'ai', 'ml']
This is a quick way to drop None values and empty strings.
map and filter vs comprehensions
Most Python developers prefer list comprehensions for these tasks because they’re more readable:
nums: list[int] = [1, 2, 3, 4, 5]
# map (less Pythonic)
squares: list[int] = list(map(lambda n: n * n, nums))
# comprehension (preferred)
squares: list[int] = [n * n for n in nums]
# filter
evens: list[int] = list(filter(lambda n: n % 2 == 0, nums))
# comprehension
evens: list[int] = [n for n in nums if n % 2 == 0]
For simple cases, comprehensions win. Use map/filter when:
- The function is already named (no lambda needed):
list(map(int, strings)) - You’re passing the result to something that takes an iterator anyway (no
list()wrap needed) - You’re piping through multiple stages and want the lazy evaluation
A practical example — clean and parse data
You’ll often do something like this when cleaning data:
raw_lines: list[str] = [" 42 ", "", "17", " ", "9"]
# strip whitespace, drop empty lines, convert to int
parsed: list[int] = [int(line.strip()) for line in raw_lines if line.strip()]
print(parsed) # [42, 17, 9]
The same with map/filter:
parsed = list(map(int, filter(lambda s: s.strip(), map(str.strip, raw_lines))))
The comprehension wins on clarity. This is the typical pattern.
What’s next
You can transform and filter collections in one line. Next, two more functional tools — zip and enumerate.