Enumerable#partition

Updated April 1, 2026 · Enumerable
ruby enumerable partition split stdlib

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

MethodReturnsDescription
selectOne arrayOnly the matching elements
partitionTwo-element arrayBoth 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

MethodReturnsKey type
partitionTwo-element arrayBoolean (truthy/falsy)
group_byHashAny 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