You have parsed flags — {"level": "ERROR", "limit": 100}. Now take a list of log dicts and apply the config. What order matters?
Filter by level first, then take the top N. If you limited first, the slice would happen on the unfiltered list and you could end up with zero matches inside a 100-entry window.
Exactly right — and that ordering is the whole pedagogy of this lesson. Two conditionals, two operations:
result = logs
if config.get("level"):
result = [log for log in result if log.get("level") == config["level"]]
if config.get("limit") is not None:
result = result[:config["limit"]]dict.get(key) returns None if the key is missing or explicitly set to None — so unset flags simply skip their block.
Why is not None for limit? I'd have used if config.get("limit"):.
Because 0 is falsy in Python. if config.get("limit"): would skip the block when limit is 0 — but 0 is a legal limit that means "return nothing." is not None checks presence explicitly, regardless of truthiness.
So the filter-then-limit order is intentional — it's the difference between "top 100 errors" and "errors among the top 100 entries."
Those are two completely different queries. Filter-then-limit gives you the first reading. Limit-then-filter gives you the second. Pick consciously. Same two operations, different order:
if config.get("limit") is not None:
result = result[:config["limit"]]And adding new flags is trivial — each one is a new if block that doesn't affect the others. --after, --ip, --host — all just slot in.
That's the composable config shape. Each flag is independent logic; the order of blocks defines the pipeline order. Clean, testable, and easy to extend.
TL;DR: filter before you limit — otherwise the limit applies to the wrong set.
config.get(key) — returns value or None if missingis not None — distinguishes 0 from absent| Order | Effect |
|---|---|
| filter → limit | top N matching entries |
| limit → filter | matching entries within the first N |
Choose deliberately — these are different queries, not equivalent.
You have parsed flags — {"level": "ERROR", "limit": 100}. Now take a list of log dicts and apply the config. What order matters?
Filter by level first, then take the top N. If you limited first, the slice would happen on the unfiltered list and you could end up with zero matches inside a 100-entry window.
Exactly right — and that ordering is the whole pedagogy of this lesson. Two conditionals, two operations:
result = logs
if config.get("level"):
result = [log for log in result if log.get("level") == config["level"]]
if config.get("limit") is not None:
result = result[:config["limit"]]dict.get(key) returns None if the key is missing or explicitly set to None — so unset flags simply skip their block.
Why is not None for limit? I'd have used if config.get("limit"):.
Because 0 is falsy in Python. if config.get("limit"): would skip the block when limit is 0 — but 0 is a legal limit that means "return nothing." is not None checks presence explicitly, regardless of truthiness.
So the filter-then-limit order is intentional — it's the difference between "top 100 errors" and "errors among the top 100 entries."
Those are two completely different queries. Filter-then-limit gives you the first reading. Limit-then-filter gives you the second. Pick consciously. Same two operations, different order:
if config.get("limit") is not None:
result = result[:config["limit"]]And adding new flags is trivial — each one is a new if block that doesn't affect the others. --after, --ip, --host — all just slot in.
That's the composable config shape. Each flag is independent logic; the order of blocks defines the pipeline order. Clean, testable, and easy to extend.
TL;DR: filter before you limit — otherwise the limit applies to the wrong set.
config.get(key) — returns value or None if missingis not None — distinguishes 0 from absent| Order | Effect |
|---|---|
| filter → limit | top N matching entries |
| limit → filter | matching entries within the first N |
Choose deliberately — these are different queries, not equivalent.
Write `apply_config(logs, config)` where `config` is a dict from argparse (keys `level` and `limit`, values may be `None`). Filter `logs` to entries where `log["level"] == config["level"]` when `level` is set, then slice to `config["limit"]` entries when `limit` is not `None`. Apply filter before limit.
Tap each step for scaffolded hints.
No blank-editor panic.