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.

Toggle theme (T)