Array#compact

Updated April 1, 2026 · Array Methods
ruby array compact nil stdlib

What does compact do?

compact returns a new array with all nil values removed. It does not modify the original array — the caller gets a cleaned copy while the original stays intact.

This is useful when working with data that may contain placeholder nil values, such as arrays built from sparse inputs, API responses, or failed operations.

Array#compact is defined directly on the Array class but behaves identically to Enumerable#compact — Array includes Enumerable, and Array overrides compact with an optimised implementation.

Basic Usage

Removing nil values

[1, nil, 2, nil, 3].compact  # => [1, 2, 3]

[nil, nil, nil].compact     # => []

[1, 2, 3].compact            # => [1, 2, 3]

With mixed data types

["hello", nil, 42, nil, "world"].compact  # => ["hello", 42, "world"]

[false, nil, 0, "", []].compact           # => [false, 0, "", []]

Note that compact only removes nil — it leaves all other falsy values (false, 0, "", []) untouched. This is the key distinction from reject.

The Bang Variant: compact!

The compact! method mutates the original array in place. If any nil values were removed, it returns the same array. If no nil values existed, it returns nil.

numbers = [1, nil, 2, nil, 3]

result = numbers.compact!

numbers  # => [1, 2, 3]
result   # => [1, 2, 3]
result.equal?(numbers)  # => true  (same object)

When compact! returns nil

If the array contains no nil values, compact! returns nil and leaves the array unchanged:

numbers = [1, 2, 3]

result = numbers.compact!
result   # => nil
numbers  # => [1, 2, 3]

Comparing compact vs compact!

original = [1, nil, 2, nil, 3]

# compact returns a new array, original unchanged
cleaned = original.compact
original  # => [1, nil, 2, nil, 3]
cleaned   # => [1, 2, 3]

# compact! modifies the original
mutated = original.compact!
original  # => [1, 2, 3]
mutated   # => [1, 2, 3]
mutated.equal?(original)  # => true

compact vs reject

Both compact and reject remove elements from an array, but they differ in what they target:

MethodRemoves
compactOnly nil values
rejectAny element where the block evaluates to falsy
data = [1, nil, 2, false, 3, "", 4]

data.compact              # => [1, 2, false, 3, "", 4]  (only nil removed)
data.reject(&:nil?)       # => [1, 2, false, 3, "", 4]  (same as compact)
data.reject { |x| !x }    # => [1, 2, 3, 4]             (all falsy values removed)

For removing only nil, compact is more direct and readable than reject(&:nil?).

Performance Considerations

compact is optimised to remove only nil values without evaluating a block for each element. This makes it faster and more memory-efficient than the equivalent reject(&:nil?).

require "benchmark"

array = [1, 2, nil, 3, nil, 4, nil] * 1000

Benchmark.measure do
  array.compact
end.real   # => ~0.001s

Benchmark.measure do
  array.reject(&:nil?)
end.real   # => ~0.015s

The compact! variant avoids allocating a new array, which can matter for large arrays or in memory-constrained contexts. However, the iteration cost is the same for both — the performance difference is solely in allocation.

Edge Cases

Empty array

[].compact    # => []
[].compact!   # => nil

No nil values

[1, 2, 3].compact    # => [1, 2, 3]
[1, 2, 3].compact!    # => nil  (no changes, returns nil)

All nil values

[nil, nil, nil].compact    # => []
[nil, nil, nil].compact!    # => []

Single nil element

[nil].compact    # => []
[nil].compact!   # => []

Practical Examples

Cleaning API response data

When parsing JSON arrays, missing fields often become nil:

responses = [
  { name: "Alice", email: "alice@example.com", phone: nil },
  { name: "Bob", email: nil, phone: "555-0100" },
  { name: "Carol", email: "carol@example.com", phone: "555-0101" }
]

responses.map { |r| r[:email] }.compact
# => ["alice@example.com", "carol@example.com"]

Removing failed operations

When building an array from operations that may fail:

results = [
  find_user(1),   # => #<User id=1>
  find_user(999), # => nil (not found)
  find_user(2),   # => #<User id=2>
  find_user(888), # => nil (not found)
  find_user(3)    # => #<User id=3>
]

valid_users = results.compact
valid_users  # => [#<User id=1>, #<User id=2>, #<User id=3>]

Preparing for iteration

Some operations fail or behave unexpectedly with nil in the array:

data = [1, nil, 2, nil, 3]

# safe: sum skips nil naturally
data.compact.sum  # => 6

# unsafe: would fail on nil
data.sum  # => nil (no error, but wrong result)

Chaining with other array methods

numbers = [1, nil, 2, nil, 3, nil, 4, nil, 5]

numbers.compact.select(&:odd?)          # => [1, 3, 5]
numbers.compact.map { |n| n * 2 }       # => [2, 4, 6, 8, 10]
numbers.compact.uniq                     # => [1, 2, 3, 4, 5]

See Also