1.2.33 · D4Introduction to Programming (Python)

Exercises — Built-in functions — map, filter, zip, enumerate, sorted, reversed, min, max, sum, any, all

2,389 words11 min readBack to topic

Before we start, one reminder in plain words: lazy means the tool (map, filter, zip, enumerate, reversed) does no work until you ask — so we wrap the answer in list(...) to force it to hand over its items. When you see list(...) around an answer, that is the "show me the goods" button.


L1 — Recognition

Here you just name the right tool. No code to run — pick the built-in whose one-sentence job matches the request.

Exercise 1.1

For each task, name the single best built-in:

  • (a) "Double every number in a list."
  • (b) "Keep only the positive numbers."
  • (c) "Pair each name with its score."
  • (d) "Is there at least one failing grade (< 40)?"
  • (e) "Print a numbered list, starting at 1."
Recall Solution 1.1
  • (a) map — its one job is "apply f to each item".
  • (b) filter — its one job is "keep items where a test is truthy".
  • (c) zip — pairs items position-by-position.
  • (d) any — "is at least one truthy?" (feed it the booleans grade < 40).
  • (e) enumerate(..., start=1) — hands you (index, item) together.

Exercise 1.2

Which of these return a lazy iterator (you must wrap in list() to see values), and which return a finished value right away? map, sorted, filter, sum, zip, enumerate, reversed, max, any, all.

Recall Solution 1.2

Lazy iterators (need list()): map, filter, zip, enumerate, reversed. Finished values right away: sorted (a real list), sum (a number), max (one element), any (a bool), all (a bool). Memory hook: the five lazy ones are the pipeline tools that feed into something; the reducers/orderers hand you a concrete answer.


L2 — Application

Now write and evaluate short expressions.

Exercise 2.1

Using map and a lambda, produce the list of cubes of [1, 2, 3, 4].

Recall Solution 2.1
list(map(lambda x: x**3, [1, 2, 3, 4]))   # [1, 8, 27, 64]

Why list(...)? map is lazy — without it you'd see <map object ...>. The lambda is the rule "cube me"; map applies it to each item.

Exercise 2.2

Keep only the strings longer than 3 characters from ['hi', 'yes', 'hello', 'ok', 'world'].

Recall Solution 2.2
list(filter(lambda s: len(s) > 3, ['hi', 'yes', 'hello', 'ok', 'world']))
# ['hello', 'world']

Why this? filter keeps each item whose test is truthy. len(s) > 3 is True only for 'hello' (5) and 'world' (5); 'yes' has length 3, which is not > 3, so it drops.

Exercise 2.3

Given names = ['Ann', 'Bo', 'Cy'] and ages = [30, 25, 40, 99], build a list of (name, age) pairs. How many pairs, and why?

Recall Solution 2.3
list(zip(['Ann','Bo','Cy'], [30, 25, 40, 99]))
# [('Ann', 30), ('Bo', 25), ('Cy', 40)]

Three pairs. zip stops at the shortest iterable. names has 3 items, ages has 4, so the lonely 99 (no matching name) is dropped.

Exercise 2.4

Sort ['pear', 'fig', 'banana', 'kiwi'] alphabetically, then by length (shortest first).

Recall Solution 2.4
sorted(['pear','fig','banana','kiwi'])            # ['banana', 'fig', 'kiwi', 'pear']
sorted(['pear','fig','banana','kiwi'], key=len)   # ['fig', 'pear', 'kiwi', 'banana']

First line: default order is alphabetical for strings, so banana < fig < kiwi < pear. Second line: key=len sorts by each word's length: fig(3), then the two length-4 words. 'pear' came before 'kiwi' in the input, and Python's sort is stable, so ties keep their original order — that's why pear lands before kiwi.


L3 — Analysis

Predict outputs and explain the why — this is where laziness and edge cases bite.

Exercise 3.1

Predict the two outputs and explain the difference:

m = map(str, [1, 2, 3])
print(list(m))
print(list(m))
Recall Solution 3.1
['1', '2', '3']
[]

Why? map returns a single-use lazy iterator. The first list(m) walks it to the end, consuming it. The second list(m) finds it already exhausted — nothing left → empty list. Fix pattern: materialize once: m = list(map(str, [1,2,3])), then reuse m freely.

Exercise 3.2

Without running, give the value of each and why:

any([])          # (a)
all([])          # (b)
any([0, '', None])   # (c)
all([1, 2, 0, 3])    # (d)
Recall Solution 3.2
  • (a) any([]) → False. any is OR over items; the identity of OR is False, so an empty OR is False.
  • (b) all([]) → True. all is AND over items; the identity of AND is True — this is vacuous truth.
  • (c) any([0, '', None]) → False. All three are falsy (zero, empty string, None), so "is any truthy?" → no.
  • (d) all([1, 2, 0, 3]) → False. all short-circuits at the first falsy item — 0 — and returns False immediately, never even looking at 3. See Truthiness in Python.

Exercise 3.3

Given pairs = [(1,'a'), (2,'b'), (3,'c')], what do nums and letters become?

nums, letters = zip(*pairs)
Recall Solution 3.3
nums    # (1, 2, 3)
letters # ('a', 'b', 'c')

Why? *pairs spreads the list, so the call becomes zip((1,'a'), (2,'b'), (3,'c')). zip pairs column-wise: first slots (1,2,3), second slots ('a','b','c'). This "unzips" what zip originally joined.


L4 — Synthesis

Combine multiple tools into one clean pipeline.

Exercise 4.1

From [3, -1, 4, -1, 5, -9, 2, 6], use filter then sum to get the total of only the positive numbers.

Recall Solution 4.1
data = [3, -1, 4, -1, 5, -9, 2, 6]
sum(filter(lambda x: x > 0, data))   # 20

Why chain? filter keeps the positives [3, 4, 5, 2, 6]; sum collapses them to one total. 3+4+5+2+6 = 20. Note sum happily eats a lazy filter iterator — no list() needed because sum consumes it directly.

Exercise 4.2

Given people = [('Ann',30), ('Bo',25), ('Cy',40), ('Di',40)], find the name of the oldest person. On a tie, who wins?

Recall Solution 4.2
people = [('Ann',30), ('Bo',25), ('Cy',40), ('Di',40)]
max(people, key=lambda p: p[1])   # ('Cy', 40)

Why key? Without it, max compares whole tuples. key=lambda p: p[1] tells it "compare by age (index 1)". The oldest age is 40, shared by Cy and Di. max returns the FIRST maximal element it meets, and Cy appears before Di, so Cy wins the tie. To get just the name: wrap with [0].

Exercise 4.3

Build a leaderboard string. Given scores = [('Ann',90), ('Cy',70), ('Bo',85)], produce a list like ['1. Ann (90)', '2. Bo (85)', '3. Cy (70)'] — sorted highest score first, numbered from 1.

Recall Solution 4.3
scores = [('Ann',90), ('Cy',70), ('Bo',85)]
ranked = sorted(scores, key=lambda p: p[1], reverse=True)
# [('Ann',90), ('Bo',85), ('Cy',70)]
board = [f"{i}. {name} ({sc})" for i, (name, sc) in enumerate(ranked, start=1)]
# ['1. Ann (90)', '2. Bo (85)', '3. Cy (70)']

Why each tool? sorted(..., key, reverse=True) orders by score, biggest first. enumerate(..., start=1) hands us the rank number and the pair together, so we never do the buggy range(len(...)) dance. The comprehension formats each line. See the figure below.

Figure — Built-in functions — map, filter, zip, enumerate, sorted, reversed, min, max, sum, any, all

L5 — Mastery

Full multi-step problems. Reason carefully before revealing.

Exercise 5.1 — Word frequency winner

Given words = ['fig', 'pear', 'fig', 'kiwi', 'pear', 'fig'], find the most frequent word using only built-ins (set, max, and a key).

Recall Solution 5.1
words = ['fig', 'pear', 'fig', 'kiwi', 'pear', 'fig']
max(set(words), key=words.count)   # 'fig'

Why? set(words) gives the distinct candidates {'fig','pear','kiwi'}. key=words.count maps each candidate to how many times it appears: fig→3, pear→2, kiwi→1. max then returns the candidate with the biggest count — fig. Edge note: on a tie, max returns the first maximal candidate it encounters — and set iteration order is not guaranteed, so don't rely on which tie wins here.

Exercise 5.2 — Validate a grid

A grid is a list of rows (lists). It's valid if every row has the same length. Write a one-line check using all, map, and set. Test it on [[1,2,3],[4,5,6]] and [[1,2],[3,4,5]].

Recall Solution 5.2
def rectangular(grid):
    return len(set(map(len, grid))) == 1
 
rectangular([[1,2,3],[4,5,6]])   # True
rectangular([[1,2],[3,4,5]])     # False

Why? map(len, grid) gives the length of each row: [3, 3] vs [2, 3]. Wrapping in set(...) keeps only distinct lengths: {3} vs {2, 3}. If all rows share a length, the set has exactly one element → valid. (You could also phrase it with all: all(len(r) == len(grid[0]) for r in grid) — both are correct.)

Exercise 5.3 — Running maximum via zip

Given a = [3, 1, 4, 1, 5] and b = [2, 7, 0, 8, 5], build a list holding the larger value at each position (element-wise max).

Recall Solution 5.3
a = [3, 1, 4, 1, 5]
b = [2, 7, 0, 8, 5]
list(map(lambda pair: max(pair), zip(a, b)))
# [3, 7, 4, 8, 5]
# equivalently: [max(x, y) for x, y in zip(a, b)]

Why chain zip + max? zip(a, b) glues them into position-pairs (3,2),(1,7),(4,0),(1,8),(5,5). Applying max to each pair picks the larger: 3,7,4,8,5. At the last position both are 5, so max returns 5 — ties are harmless here.

Exercise 5.4 — Reverse-order ranked flags

Given temps = [18, 25, 30, 12, 22], list (rank_from_hottest, temp) pairs. Rank 1 = hottest.

Recall Solution 5.4
temps = [18, 25, 30, 12, 22]
hottest_first = sorted(temps, reverse=True)   # [30, 25, 22, 18, 12]
list(enumerate(hottest_first, start=1))
# [(1, 30), (2, 25), (3, 22), (4, 18), (5, 12)]

Why reverse=True instead of reversed? sorted(temps, reverse=True) sorts and flips in one step. Using reversed(sorted(temps)) would also work (sort ascending, then walk backwards) — same result — but reverse=True is one clear step. Then enumerate(..., start=1) numbers them from the hottest.


Recall One-screen recap

Pipeline (lazy): map transform · filter keep · zip pair · enumerate number · reversed back-to-front. Answers (eager): sorted new ordered list · min/max extreme (use key!) · sum total · any/all OR/AND with short-circuit. Gotchas: iterators are single-use → list() once; key orders never removes; all([])=True, any([])=False.