Skip to content

← Getting Started with Tests and Type Checks step 11 of 13

Easy Primitives

Placement 4/6: Handle a Generator You Can Read Only Once

Solve this without the hints and you can skim the generators track.

A generator produces its values one at a time, as you ask for them, and you can read it only once. After its last value it is exhausted, and any later loop over it gets nothing. You can loop over a list as often as you like. Iterable[int], the type for anything a for loop can go over, accepts both.

squares = (n * n for n in [1, 2, 3])
print(sum(squares))  # 14
print(sum(squares))  # 0, because the generator is exhausted

The starter's summarise loops over rows four times, once to count, once to sum and twice to filter. Given a generator, only the first loop sees any values, so both result lists come back empty.

What to write

Fix summarise(rows: Iterable[int]) -> dict[str, list[int]]. It splits the values around their mean, the sum divided by the count.

  • "above" holds the values strictly greater than the mean.
  • "at_or_below" holds every other value, including any equal to the mean.
  • Both lists keep duplicates and the input order.
  • Empty input returns {"above": [], "at_or_below": []}.

Read rows once into a tuple or a list, and use that copy for every pass. The copy holds every value in memory, which is fine here because the input is small and finite.

Don't compute the mean as a float. With total as the sum and count as the number of values, test row * count > total instead of row > total / count. The two tests agree because count is positive. Integer arithmetic never rounds, so a value equal to the mean always goes in "at_or_below".

The driver

Leave solve as it is. It passes summarise a generator of squares:

def solve(values: list[int]) -> dict[str, list[int]]:
    return summarise(value * value for value in values)

So don't square the values again. For values = [1, 2, 3], the rows are 1, 4 and 9, their mean is about 4.67, and the result is {"above": [9], "at_or_below": [1, 4]}.

Edge cases

  • A single value equals the mean, so it goes in "at_or_below".
  • If every value is the same, "above" is empty.
  • Check for empty input after you copy the values. A generator object is always truthy, even when it has no values left, so if not rows cannot tell you it is empty.