Enumerable#partition
What does partition do?
partition splits a collection into two arrays based on a condition. The first array contains elements for which the block evaluates to truthy. The second array contains the rest.
It always returns a two-element array: [matching, non_matching]. This makes it ideal when you need both the “yes” and “no” groups from a single pass over the collection.
Basic Usage
With a block
[1, 2, 3, 4, 5].partition { |n| n > 3 }
# => [[4, 5], [1, 2, 3]]
evens, odds = [1, 2, 3, 4, 5].partition { |n| n.even? }
evens # => [2, 4]
odds # => [1, 3, 5]
With predicate patterns
You can pass a pattern object instead of a block. Each element is tested against the pattern using ===:
[1, 2, 3, 4].partition(Integer) # => [[1, 2, 3, 4], []]
[1, 2, 3, 4].partition(String) # => [[], [1, 2, 3, 4]]
words = ["apple", "banana", "cherry", "apricot"]
short, long = words.partition { |w| w.length <= 5 }
short # => ["apple"]
long # => ["banana", "cherry", "apricot"]
Without a block
When called without a block or argument, partition returns an Enumerator:
[1, 2, 3].partition # => #<Enumerator: [1, 2, 3]:partition>
This allows chaining with other Enumerable methods:
[1, 2, 3, 4, 5]
.partition.with_index { |n, i| i.even? }
# => [[1, 3, 5], [2, 4]]
Practical Examples
Separating valid and invalid data
records = [
{ id: 1, email: "alice@example.com", confirmed: true },
{ id: 2, email: "bob@example.com", confirmed: false },
{ id: 3, email: nil, confirmed: true },
{ id: 4, email: "carol@example.com", confirmed: true }
]
valid, invalid = records.partition { |r| r[:email] && r[:confirmed] }
valid # => [{ id: 1, ... }, { id: 4, ... }]
invalid # => [{ id: 2, ... }, { id: 3, ... }]
Even and odd numbers
numbers = (1..10).to_a
evens, odds = numbers.partition(&:even?)
evens # => [2, 4, 6, 8, 10]
odds # => [1, 3, 5, 7, 9]
Pass and fail scores
scores = { alice: 85, bob: 55, charlie: 92, diana: 48, eve: 70 }
passing, failing = scores.partition { |_, score| score >= 60 }
passing # => [[:alice, 85], [:charlie, 92], [:eve, 70]]
failing # => [[:bob, 55], [:diana, 48]]
Splitting by category
products = [
{ name: "Widget", price: 100, category: "electronics" },
{ name: "Screwdriver", price: 15, category: "tools" },
{ name: "Monitor", price: 250, category: "electronics" },
{ name: "Hammer", price: 30, category: "tools" },
{ name: "Keyboard", price: 80, category: "electronics" }
]
electronics, other = products.partition { |p| p[:category] == "electronics" }
electronics.count # => 3
other.count # => 2
Processing both groups differently
Because partition returns two arrays, you can immediately process each group:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
low, high = numbers.partition { |n| n <= 5 }
low_sum = low.sum # => 15
high_doubled = high.map { |n| n * 2 } # => [12, 14, 16, 18, 20]
partition vs select
| Method | Returns | Description |
|---|---|---|
select | One array | Only the matching elements |
partition | Two-element array | Both matching and non-matching |
select discards the non-matching elements. partition keeps both groups:
numbers = [1, 2, 3, 4, 5]
selected = numbers.select { |n| n > 3 }
# => [4, 5]
partitioned = numbers.partition { |n| n > 3 }
# => [[4, 5], [1, 2, 3]]
Using select and reject together is equivalent to partition, but partition is more efficient — it makes a single pass through the collection:
numbers = [1, 2, 3, 4, 5]
# Two passes:
matching = numbers.select { |n| n > 3 }
non_matching = numbers.reject { |n| n > 3 }
# One pass (equivalent result):
matching, non_matching = numbers.partition { |n| n > 3 }
partition vs group_by
| Method | Returns | Key type |
|---|---|---|
partition | Two-element array | Boolean (truthy/falsy) |
group_by | Hash | Any value the block returns |
partition always produces exactly two groups based on truthiness. group_by produces as many groups as there are unique return values:
numbers = [1, 2, 3, 4, 5, 6]
partitioned = numbers.partition { |n| n.even? }
# => [[2, 4, 6], [1, 3, 5]] (always exactly 2 groups)
grouped = numbers.group_by { |n| n.even? }
# => {true=>[2, 4, 6], false=>[1, 3, 5]} (hash, boolean keys)
When you only need two groups and don’t need the hash structure, partition is cleaner:
# partition gives you direct arrays to work with
passing, failing = scores.partition { |_, s| s >= 60 }
average_pass = passing.sum { |_, s| s } / passing.size
# group_by requires extracting values from the hash
grouped = scores.group_by { |_, s| s >= 60 }
average_pass = grouped[true].sum / grouped[true].size
Performance Considerations
partition iterates through the entire collection once. Time complexity is O(n).
[1, 2, 3, 4, 5].partition { |n| n > 2 }
# Checks: 1, 2, 3, 4, 5 (all five iterations)
Since partition always makes a full pass, it cannot short-circuit like find or any?. If you only need one of the two groups, using select or reject directly may be more appropriate.
See Also
- /reference/enumerable/enumerable-select/ — Returns only elements for which the block is truthy
- /reference/enumerable/enumerable-reject/ — Returns elements for which the block is falsy
- /reference/enumerable/enumerable-group-by/ — Groups elements by any criteria into a hash
- /reference/enumerable/enumerable-none/ — Checks if no elements match a condition