← Home

090. Map() And Filter()

Apply functions to iterables without explicit loops

090. Map() And Filter()

He reads through old Python RAM monitoring scripts from Stack Overflow and they use map and filter everywhere. He needs to understand them to maintain this code.

processes = [
    {"name": "chrome",  "rss_mb": 620, "running": True},
    {"name": "python",  "rss_mb": 85,  "running": False},
    {"name": "slack",   "rss_mb": 310, "running": True},
]

# map: transform every element
rss_list = list(map(lambda p: p["rss_mb"], processes))
# → [620, 85, 310]

# filter: keep elements where function returns True
running = list(filter(lambda p: p["running"], processes))
# → [chrome, slack]

# Equivalent comprehensions (modern preferred style):
rss_list = [p["rss_mb"] for p in processes]
running  = [p for p in processes if p["running"]]

💡 In new code, prefer list comprehensions. But every Python codebase older than 2012 uses map/filter — you will encounter them.


💡 Fun fact: map() and filter() are concepts from functional programming dating to Lisp in the 1950s. In Python 2, they returned lists immediately. In Python 3, Guido van Rossum made them return lazy iterators to save memory — a decision that broke some Python 2 code during the migration and is why you need list(map(...)) to get a concrete list.

⚠️ Watch out: Because map() and filter() return iterators (not lists), you can only iterate them once. If you store a map object and loop over it twice, the second loop produces nothing. This surprises developers coming from Python 2 or languages where these functions return collections.

🤔 Think about it: list(map(lambda x: x * 2, numbers)) and [x * 2 for x in numbers] do the same thing. The list comprehension is generally considered more Pythonic. But map(func, iterable) shines when func is already a named function — list(map(str.upper, words)) is cleaner than [w.upper() for w in words]. When would you choose map over a comprehension?

Learning objectives

  • Apply map() to transform every element of an iterable
  • Use filter() to select elements based on a condition
  • Wrap map/filter in list() to get a concrete list

Key concepts

  • map
  • filter
  • lambda
  • iterator

Try it

Concept detail

map(function, iterable) applies function to every element.

list(map(lambda x: x * 2, [1, 2, 3]))
# → [2, 4, 6]

list(map(str.upper, ["hello", "world"]))
# → ["HELLO", "WORLD"]  (passing a method reference directly)

filter(function, iterable) keeps elements where function returns True.

list(filter(lambda x: x > 0, [-1, 2, -3, 4]))
# → [2, 4]

list(filter(None, [0, 1, "", "hello", None, True]))
# → [1, "hello", True]  (None as function filters falsy values)

Both return ITERATORS (lazy) — wrap with list() to get a concrete list.

Equivalent list comprehensions: list(map(lambda x: x2, items)) ≡ [x2 for x in items] list(filter(lambda x: x>0, items)) ≡ [x for x in items if x > 0]

WHY know map/filter even if comprehensions are preferred:

  • Older codebases (especially pre-Python 3.0 migrations) use them everywhere
  • Functional programming style: map/filter chain naturally
  • Some APIs accept callables rather than comprehensions
  • map is slightly faster than a comprehension for very simple transforms

map vs filter — different jobs: map → transform: [2, 4, 6, 8] (same length as input) filter → select: [2, 4] (length ≤ input)

Solution

def double_all(numbers):
    return list(map(lambda x: x * 2, numbers))

def positive_only(numbers):
    return list(filter(lambda x: x > 0, numbers))

def normalize(words):
    return list(map(lambda w: w.strip().lower(), words))

Tests

def test_double_all():
    assert double_all([1, 2, 3, 4]) == [2, 4, 6, 8]

def test_double_all_negative():
    assert double_all([-1, 0, 1]) == [-2, 0, 2]

def test_double_all_empty():
    assert double_all([]) == []

def test_positive_only():
    assert positive_only([-1, 0, 2, -3, 5]) == [2, 5]

def test_positive_only_excludes_zero():
    # broken code keeps 0 (>= 0); correct code excludes it (> 0)
    assert positive_only([0]) == []
    assert positive_only([-1, 0, 1]) == [1]

def test_positive_only_all_negative():
    assert positive_only([-1, -2]) == []

def test_normalize():
    result = normalize(["  Hello  ", "WORLD", " Python "])
    assert result == ["hello", "world", "python"]

def test_normalize_already_clean():
    assert normalize(["abc"]) == ["abc"]

Resources