What is the best way to find all combinations of items in an array?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Finding combinations from an array is a classic task in search, testing, recommendation, and feature engineering. The best approach depends on whether you need all combination sizes or only size k, and whether memory is limited. Combination counts grow rapidly, so algorithm choice must include output-size reality.
Clarify the Exact Combination Problem
Before implementation, define these requirements:
- combinations of fixed size
kor all sizes - combinations with or without duplicate input values
- order-insensitive combinations or order-sensitive permutations
- streaming output or full materialization
A lot of bugs come from solving permutations when combinations were required.
Count Growth and Complexity
For fixed size k, number of combinations is n choose k. For all non-empty sizes, total combinations are 2^n - 1. This growth dominates runtime regardless of language.
Practical implication:
- small arrays can materialize all combinations safely
- medium arrays should often stream combinations
- large arrays require pruning, sampling, or constraints
No implementation can avoid output-size cost once all combinations are requested.
That is why "best way" usually means "best way for the expected size and downstream use." A mathematically elegant algorithm still becomes impractical if the calling code insists on materializing millions of combinations at once.
Python Best Practice with itertools
For Python, itertools.combinations is usually the cleanest and fastest approach.
Use generator iteration when you do not need full list in memory.
Generate All Sizes
If you need all non-empty combinations, chain over k values.
This remains memory-efficient because results are yielded lazily.
Recursive Strategy for Languages Without Built-Ins
In Java or C-like languages, recursion is a common pattern.
This gives predictable order and clear control over size k.
Handling Duplicate Values in Input
If input has duplicates and you need unique combination values, deduplicate first or skip repeated values during traversal.
Simple Python approach:
If index-level uniqueness matters, do not deduplicate values globally. Define uniqueness rule explicitly.
Streaming Versus Materialization
Materializing all combinations can exhaust memory fast.
Prefer stream processing pipelines:
Streaming is often enough when you only aggregate or filter results.
If downstream logic only needs counts, maxima, or filtered subsets, streaming is almost always the better default because it keeps memory usage predictable.
Filtering During Generation
Apply pruning early when possible.
Filtering at generation time avoids storing large intermediate sets.
Choosing the Best Method
Use this rule of thumb:
- Python fixed-size combinations:
itertools.combinations - all sizes with streaming: generator over
k - languages without built-ins: recursive backtracking
- huge search spaces: constrain, sample, or prune
Best method is the one matching both correctness and resource limits.
Common Pitfalls
- Confusing combinations with permutations.
- Requesting all combinations without estimating output size.
- Materializing giant combination lists unnecessarily.
- Ignoring duplicate-value semantics in input.
- Using recursion without stack or depth considerations in large cases.
Summary
- Define combination requirements clearly before coding.
- Output-size growth is the main scalability constraint.
- Use built-in combinatorics tools where available.
- Prefer streaming and early filtering for larger datasets.
- Treat duplicate handling rules as part of core contract.

