Runs of equal neighbours: groupby
`itertools.groupby` walks a sequence and hands you each run of neighbouring items that share a key. It finds streaks in time-ordered data, and groups a whole list once the list is sorted by the same key.
- Loop over
groupby(items, key=…)as(key, run)pairs and turn a run into a list before measuring it - Sort by the same key first when every item with that key must land in one group
- Recognise that a run is used up once groupby moves on
A health check pings a server every minute and logs up or down. The useful summary isn't *how many downs in total* but *when did it go down, and for how long*: the runs of equal values next to each other.
from itertools import groupby gives a tool for exactly that. groupby(items) yields one (key, run) pair per run of equal neighbours. run is not a list but an *iterator*: a one-pass stream of that run's items, which you can loop over once or turn into a list with list(run).
Run each example to see what it prints. Running examples doesn't count toward progress.
from itertools import groupby checks = ["up", "up", "down", "down", "down", "up", "up", "up"] for status, run in groupby(checks): print(status, len(list(run)))
from itertools import groupby for status, run in groupby(["up", "up", "down"]): print(status, len(run))
To group a whole list by a key, sort it by that key first, so equal keys sit next to each other, then group with the same key function. key= works as it does for sorted: groupby compares what the key returns, not the items themselves.
from itertools import groupby def daily_totals(orders): ordered = sorted(orders, key=lambda o: o["date"]) totals = [] for date, day_orders in groupby(ordered, key=lambda o: o["date"]): totals.append((date, sum(o["total"] for o in day_orders))) return totals orders = [ {"date": "2025-03-04", "total": 35.5}, {"date": "2025-03-03", "total": 20.25}, {"date": "2025-03-04", "total": 14.5}, {"date": "2025-03-03", "total": 9.75}, {"date": "2025-03-05", "total": 60.0}, ] print(daily_totals(orders))
When to use it: streaks and periods in ordered data (uptime, consecutive hot days, repeated log lines), and per-key summaries of data that is sorted anyway. For grouping unsorted data in one pass, the defaultdict(list) from two lessons ago is simpler. Mistakes to avoid: forgetting to sort (the same key shows up in several groups); sorting by one key and grouping by another; and keeping a run to read later: once groupby moves to the next run, the old one is used up. Turn it into a list inside the loop if you need it again.
groupby(items, key=…)yields(key, run)for each run of neighbours with the same key.- A run is a one-pass stream:
list(run)to measure or keep it, inside the loop. - To group a whole list, sort it by the same key first.
- Next: itertools.combinations, every pair (or trio) from a list, each once.