05 / BUILDING USEFUL TOOLS · LESSON 19

Compose small reporting functions

Separate selection from summary calculation so both can be reused.

14 min · learn at your pace

Give each function one clear job

A report often needs several steps: read data, select records, calculate a summary, and write results. Keeping those jobs separate helps you test them independently and reuse the logic with another file format.

Here eligible_assets selects records, and summarise_assets calculates totals from whichever records it receives. Neither function needs to know where the data came from. Passing the selected list into the summary function makes the sequence visible at the call site.

Preserve the inputs

A caller may need the full inventory after you build a report. Avoid removing entries or changing fields in its collection. Returning a new list is enough for this task because the functions only read the dictionaries inside it. If you later change fields in returned dictionaries, consider copying those records too.

Define empty-input behaviour deliberately. An empty selection is still a valid result, with count zero and total memory zero. A configurable threshold avoids embedding a policy decision throughout the code. The capstone will use this same separation with CSV inputs, JSON policy, validation, and output files.

Try an example.py
def total_memory(rows):
    return sum(row["memory_gb"] for row in rows)

print(total_memory([{"memory_gb": 8}, {"memory_gb": 16}]))
YOUR TURN

Put it into practice.

Define eligible_assets(rows, minimum_memory=16) returning active records with memory_gb at least the threshold, sorted by name. Define summarise_assets(rows) returning {"count": number of rows, "memory_gb": total memory}. Leave inputs unchanged and handle empty lists.

See an explained solution
One way to solve it.py
def eligible_assets(rows, minimum_memory=16):
    return sorted([row for row in rows if row["active"] and row["memory_gb"] >= minimum_memory], key=lambda row: row["name"])

def summarise_assets(rows):
    return {"count": len(rows), "memory_gb": sum(row["memory_gb"] for row in rows)}

The selection function applies policy and ordering. The summary function is independent of that policy, so it can summarise the entire inventory or just a selected subset.