Warmup

  • Create a list with 2 items
  • Create a tuple with 3 items
  • Create a dictionary with 2 items

Agenda / Looking ahead

  • This week we continue and conclude with Unit 3
  • Topics: list comprehensions, 2D lists, and string operations
  • Unit 3 Quiz 2 on Thursday
  • Unit 3 Exam on Friday
  • Begin Unit 4 (functions and exceptions) on Friday

Due Dates

  • Assignment 3.6 due today

Lesson 3.7 — List Comprehensions: Map and Filter

  • Today’s goal: write clear, concise list comprehensions for mapping and filtering.
  • Why they matter: compact, expressive data transformations on lists.
  • You will convert loops to comprehensions and decide when not to use them.

Warm-Up: Convert This Loop

nums = [1, 2, 3, 4, 5]
result = []
for n in nums:
    result.append(n * n)
print(result)
  • Task: rewrite using a list comprehension.
  • Question: when is the loop clearer than the comprehension?

Syntax Basics

  • Pattern: [expression for item in iterable]
  • Example: squares of 1..5
squares = [n * n for n in range(1, 6)]
print(squares)  # [1, 4, 9, 16, 25]
  • Read left-to-right as: for each n, compute n*n and collect into a list.

Quick Practice

  • Build a list of cubes from 0..9.
  • Predict: what is len([c for c in 'python'])?
  • Code it, run it, verify your prediction.

Filtering With if

  • Pattern: [expression for item in iterable if condition]
  • Example: keep only positive numbers, then double them
data = [-3, -1, 0, 2, 4]
out = [x * 2 for x in data if x > 0]
print(out)  # [4, 8]
  • The trailing if filters items before the expression is applied.

Practice: Odd Squares Under 50

  • Build odd squares less than 50 from 1..20 using one comprehension.
  • Hint: combine if with the square expression.
  • Extension: what changes to generate even squares under 100?

Map + Filter Together

  • We often normalize values while filtering.
names = ['  ALIce', 'bob  ', '   ', 'Eve']
clean = [n.strip().title() for n in names if n.strip()]
print(clean)  # ['Alice', 'Bob', 'Eve']
  • Question: why call strip() twice? Can you avoid the second call?

Practice: Emails → Usernames

  • Given emails = ['ada@school.edu', 'ben@gmail.com', 'cai@college.edu']
  • Build a list of usernames for only .edu addresses: ['ada', 'cai']
  • Hint: str.endswith, str.split('@', 1)

Conditional Expression in Expression Part

  • Pattern: [expr_if_true if cond else expr_if_false for item in iterable]
grades = [92, 67, 81, 58]
labels = ['Pass' if g >= 70 else 'Fail' for g in grades]
print(labels)  # ['Pass', 'Fail', 'Pass', 'Fail']
  • Note: if inside expression is different from trailing filter if.

Practice: Letter Grade Buckets

  • Map numeric grades to letters in one comprehension: A/B/C/D/F.
  • Hint: nest conditional expressions carefully, or prefer a loop for clarity.

Nested Loops in Comprehensions

  • Pattern: [expr for a in A for b in B]
A = [1, 2]
B = ['x', 'y']
pairs = [(a, b) for a in A for b in B]
print(pairs)  # [(1, 'x'), (1, 'y'), (2, 'x'), (2, 'y')]
  • Flatten one level of a 2D list:
matrix = [[1, 2], [3, 4, 5], [6]]
flat = [x for row in matrix for x in row]
print(flat)  # [1, 2, 3, 4, 5, 6]

Practice: Row Sums and Flatten > Threshold

  • Given grid = [[2, -1, 3], [0, 5], [7, -2, 4]]:
  • Build row_sums list via comprehension.
  • Build pos_flat of all positive elements using a nested comprehension.

Copy vs. Alias; Shallow vs. Deep

  • Copy a list: [x for x in lst] or lst.copy() (shallow copies only).
  • Pitfall: nested lists still share inner objects.
rows = [[0]*3 for _ in range(3)]  # correct 2D init
  • Question: why is [[0]*3]*3 problematic?

Pitfalls and When Not to Use

  • Avoid side effects in comprehensions (e.g., print, append).
  • Prefer explicit loops when logic is long or branching heavily.
  • Watch readability: short, linear transforms only.

Exit Ticket — Quick Check

  • What’s the difference between [...] if cond vs. [...] if cond else ...?
  • Write a comprehension for even numbers 0..20 inclusive.
  • Flatten [[1,2],[3],[4,5]] in one line.
  • When would you avoid using a comprehension?