Filtering list under limitations
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Filtering a list is easy when you can hold everything in memory and evaluate every rule freely. It becomes harder when the task has limitations such as memory budgets, time limits, bounded output size, or expensive predicates. The right design is usually not a single clever expression; it is a filtering pipeline that spends work only where it matters.
Define the Actual Limitation First
The phrase "under limitations" is too vague unless you name the constraint. Common ones are:
- the list is too large to load fully
- only the first
Nmatches are needed - each predicate call is expensive
- filtering must finish within a time budget
- output must fit into a bounded buffer
Once the limitation is explicit, the implementation becomes much easier to reason about.
For example, if you only need the first five matches, do not filter the entire list and slice afterward:
That stops as soon as enough matches are found.
Prefer Streaming over Materializing
When memory is the limiting factor, avoid building intermediate lists. Generators let you process one item at a time.
This approach keeps memory usage small because the entire dataset is never stored at once.
The same principle applies to files and database cursors. If the data source already supports streaming, keep the pipeline streaming all the way through.
Order Predicates by Cost and Selectivity
If some conditions are much cheaper than others, evaluate cheap rejects first. That can drastically reduce total work.
Here, the cheap conditions x > 10 and x % 2 == 0 eliminate most candidates before the expensive check runs.
This is one of the simplest ways to optimize filtering without changing the output semantics.
Bound the Result Size Explicitly
Another common limitation is that only a fixed number of results can be stored or returned. Use a loop that stops once the budget is reached.
This is clearer than building the full filtered list and trimming it later.
If you are working with streams, return an iterator instead of a list so downstream code can apply its own limit lazily.
Offload Filtering Earlier When Possible
If the input comes from a database, API, or file parser, the cheapest list filter is often the one you never run locally. Push filtering down to the source when it can do the work more efficiently.
For example, instead of loading all rows and filtering in Python:
in a real database-backed system you would usually ask the database for open rows directly. The same logic applies to paginated APIs and search indexes.
Make Failure Modes Explicit
Limitations often imply compromises. If the filter can stop early due to time or memory budget, expose that behavior instead of silently returning partial results as if they were complete.
A practical interface might return both matches and a completion flag:
That is much safer than returning partial data without context.
Common Pitfalls
- Filtering the full dataset when only the first few matches are actually required.
- Materializing large intermediate lists when a generator pipeline would work.
- Running expensive predicates before cheap elimination checks.
- Applying local filtering after loading data that could have been filtered at the source.
- Returning partial results under time or memory limits without telling the caller that the result is incomplete.
Summary
- The correct filter strategy depends on the specific limitation, not on the list itself.
- Use streaming pipelines when memory is tight.
- Order predicates so cheap rejects happen before expensive checks.
- Stop early when the result size or time budget allows it.
- Make truncated or partial filtering outcomes explicit to the caller.

