Splitting a list into N parts of approximately equal length
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Splitting a list into roughly equal parts is a small problem that appears in a lot of real work. It comes up when dividing data for batch jobs, distributing items across workers, paginating results, or chunking input for APIs. The goal is usually not random splitting; it is predictable partitioning where chunk sizes differ by at most one item.
The Core Rule
If a list has m items and you want n chunks, the cleanest rule is:
- every chunk gets a base size of
m // n - the first
m % nchunks get one extra item
That guarantees two properties:
- Every element is used exactly once.
- No two chunk sizes differ by more than one.
Python's divmod() expresses this neatly.
A Reliable Implementation
Here is a straightforward implementation that returns a list of lists:
Output:
The first chunk gets the extra element because the remainder is 1. If the remainder were 2, the first two chunks would be one item longer.
Why This Beats Naive Slicing
A common first attempt is to compute chunk_size = len(items) // n and slice repeatedly using that number. The problem is that integer division drops the remainder, so some elements are lost unless you add special handling at the end.
Another weak approach is using floating-point boundaries and rounding them. That can work, but it is harder to reason about and easier to get subtly wrong when indexes repeat or skip.
The divmod() approach is better because it is exact and easy to audit in code review.
Handling More Chunks Than Items
If n is greater than the number of elements, some chunks must be empty. That is not a bug. It is the only mathematically correct result if you insist on exactly n partitions.
Output:
Whether empty chunks are acceptable depends on the use case. For worker distribution, they are often fine because idle workers simply do nothing. For user-facing pagination, you may prefer to return at most len(items) chunks instead.
Returning a Generator Instead of a Full List
If the input is large and you want to stream chunks one by one, return an iterator. The splitting logic is the same.
This is useful when each chunk is sent to a separate subsystem or processed immediately, because you do not need to materialize the outer list first.
Preserving Order Versus Balancing Work
The examples above preserve the original order of the input. That is usually what you want for deterministic processing. But keep in mind that equal item counts do not always mean equal work.
Suppose each list item represents a file and file sizes vary widely. Splitting by count may still produce very uneven processing time. In that situation, the real problem is workload balancing, not list partitioning. You may need weighted assignment or a queue-based worker pool instead.
That distinction matters because many developers try to solve a scheduling problem with a slicing function.
Practical Uses
This pattern is especially useful in three cases:
- batch API calls where each chunk becomes one request
- test-data partitioning across workers in CI
- dividing a sequence into training, validation, and holdout segments when exact boundaries matter
The function remains simple because it does one thing well: partition by count while preserving order.
Common Pitfalls
The first pitfall is forgetting to validate n. A value of zero should fail fast, not trigger a divide-by-zero error later.
The second is assuming every chunk must be non-empty. That only holds when n is less than or equal to the number of elements.
Another issue is confusing approximate equality with randomization. This algorithm does not shuffle. If you need randomized groups, shuffle the list first and then split it.
Finally, be careful with mutable nested objects. Slicing creates a new outer list, but the inner objects are still shared references. If you mutate a dictionary inside one chunk, the original object changes too.
Summary
- Use
divmod(len(items), n)to compute balanced chunk sizes exactly. - The first
remainderchunks should receive one extra item. - This guarantees chunk sizes differ by at most one element.
- Empty chunks are expected when
nexceeds the item count. - Prefer a generator form when processing large inputs incrementally.
- Equal chunk sizes do not guarantee equal processing time for uneven workloads.
Related reading
- Splitting an array finding minimum difference between the sum of two subarray in distributed environment
- SPOJ 370 - Ones and zeros ONEZERO
- spoj ARRAYSUB On Complexity Approach
- SQL multiple column ordering
- Spring-Data JPA CrudRepository returns Iterable, is it OK to cast this to List?
- Spring / RabbitMQ transaction management
- Splitting on last delimiter in Python string?
- SQLAlchemy - Getting a list of tables

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.