Stretch 2

Who stocks what

shops maps each shop name to a list of the items it stocks. Write stockists(shops) that returns a dictionary from each item to the sorted list of shops that stock it.

stockists({"north": ["milk", "bread"], "south": ["bread"]})  ->  {"milk": ["north"], "bread": ["north", "south"]}
def stockists(shops):
    ...

Best stretch of days

daily is a list of daily sales. Write best_window(daily, k) that returns the largest total over any k consecutive days. The list has at least k days.

best_window([2, 1, 5, 1, 3, 2], 3)  ->  9     (5 + 1 + 3)
def best_window(daily, k):
    ...

Validate product codes

A valid product code is three capital letters, a dash, and three digits, like BRD-001. Write valid_codes(codes) that returns the list of valid codes from the input, in order.

valid_codes(["BRD-001", "brd-001", "BRD-01", "MLK-010", "BRD001"])  ->  ["BRD-001", "MLK-010"]
def valid_codes(codes):
    ...

Transpose a table

A table is a list of rows, each a list of the same length. Write transpose(table) that returns the table with rows and columns swapped.

transpose([[1, 2, 3], [4, 5, 6]])  ->  [[1, 4], [2, 5], [3, 6]]
transpose([])                      ->  []
def transpose(table):
    ...

Running balance

entries is a list of (description, amount) tuples; positive amounts are money in, negative money out. Write ledger(entries) that returns a list of strings <description>: <balance> showing the balance after each entry, and stops early with the string "OVERDRAWN" as the last line if the balance ever goes below zero.

ledger([("sale", 50), ("rent", -30), ("sale", 10)])  ->  ["sale: 50", "rent: 20", "sale: 30"]
ledger([("sale", 10), ("rent", -30), ("sale", 100)])  ->  ["sale: 10", "rent: -20", "OVERDRAWN"]
def ledger(entries):
    ...

First unique item

Write first_unique(items) that returns the first item that appears exactly once in the list, or None if every item repeats.

first_unique(["milk", "bread", "milk", "eggs"])  ->  "bread"
first_unique(["a", "a"])                         ->  None
def first_unique(items):
    ...

Shelf totals

grid is a list of rows, each row a list of quantities on one shelf. Write shelf_report(grid) that returns a dictionary with three entries: "rows", a list of each row's total; "columns", a list of each column's total; and "max_row", the index of the row with the largest total (first on ties). The grid has at least one row and all rows have the same length.

shelf_report([[1, 2], [3, 4]])  ->  {"rows": [3, 7], "columns": [4, 6], "max_row": 1}
def shelf_report(grid):
    ...

Deepest nesting

Write max_depth(text) that returns how deeply the round brackets in text nest. Unbalanced text returns -1. Other characters are ignored.

max_depth("(a(b)(c(d)))")  ->  3
max_depth("()")            ->  1
max_depth("")              ->  0
max_depth("(()")           ->  -1
def max_depth(text):
    ...

Median price

Write median(prices) that returns the median of a non-empty list: the middle value when sorted, or the average of the two middle values when the count is even. Do not change the list you are given.

median([40, 2.5, 25])      ->  25
median([40, 2.5, 25, 10])  ->  17.5
def median(prices):
    ...

Wrap a label

Write wrap(text, width) that breaks a sentence into lines no longer than width characters, breaking only between words and fitting as many words per line as possible. Every word is shorter than width.

wrap("fresh sourdough bread baked daily", 12)  ->  ["fresh", "sourdough", "bread baked", "daily"]
def wrap(text, width):
    ...

Next: Hard.