Summarise a list of records
Most reports reduce a list of records to a few numbers and names: how many, the average, the best. One function that walks the list and returns those values can serve every report.
- Collect a count, a total and a best record in one pass over a list of dictionaries
- Return several results as a tuple, and None for an empty list
- Filter by a parameter (a genre, a minimum) inside the same pass
Runtime Films keeps one dictionary per film: title, genre, rating, number of votes, running time. A catalogue page needs a summary line: how many films, their average rating, the best one. That is a single walk over the list, carrying three things: a count, a running total, and the best record so far.
The edge case comes first: with no films there is no average and no best, so the function returns None and the caller decides what to show.
Run each example to see what it prints. Running examples doesn't count toward progress.
def summarise(films): """Return (count, average rating, best title), or None for no films.""" if len(films) == 0: return None total = 0 best = films[0] for film in films: total = total + film["rating"] if film["rating"] > best["rating"]: best = film return len(films), total / len(films), best["title"] films = [ {"title": "Wild Archive", "genre": "drama", "rating": 7.8}, {"title": "Night Harbor", "genre": "thriller", "rating": 8.1}, {"title": "Blue Dune", "genre": "sci-fi", "rating": 7.2}, ] count, average, best = summarise(films) print(f"{count} films, average {average:.2f}, best: {best}") print(summarise([]))
A parameter can narrow the summary without a second function: pass a genre, and skip the films of other genres in the same loop. Reports usually also need a threshold: a film with 85 votes and a rating of 8.4 shouldn't top a chart over films with thousands of votes. A default makes the usual threshold automatic.
def best_in_genre(films, genre, min_votes=100): best = None for film in films: if film["genre"] != genre or film["votes"] < min_votes: continue if best is None or film["rating"] > best["rating"]: best = film if best is None: return f"{genre}: no film qualifies" return f"{genre}: {best['title']} ({best['rating']}, {best['votes']} votes)" films = [ {"title": "Wild Archive", "genre": "drama", "rating": 7.8, "votes": 1520}, {"title": "Glass Echo", "genre": "drama", "rating": 8.4, "votes": 85}, {"title": "Paper Moons", "genre": "drama", "rating": 8.2, "votes": 610}, {"title": "Night Harbor", "genre": "thriller", "rating": 8.1, "votes": 940}, ] print(best_in_genre(films, "drama")) print(best_in_genre(films, "drama", min_votes=50)) print(best_in_genre(films, "comedy"))
When to use it: any *how many / what average / which is best* report over a list of records. Mistakes to avoid:
- Starting the best at
0or at an empty dictionary: start at None, or at the first record after checking the list isn't empty. - Dividing by
len(films)after filtering: divide by the number of records you actually counted. - Using
>=when the earlier record should win a tie.
- A summary is one pass that carries a count, a running total and the best record so far.
- Handle the empty list first; return None or an agreed fallback.
- Filter by parameters inside the same loop, and divide by the number of records actually counted.
- Next: a run of daily weather readings, where some days are missing.