Array#compact
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:
| Method | Removes |
|---|---|
compact | Only nil values |
reject | Any 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
- /reference/array-methods/array-reject/ — Returns a new array with elements for which the block evaluates to false
- /reference/enumerable/enumerable-select/ — Returns elements for which the block evaluates to true
- /reference/enumerable/enumerable-filter-map/ — Filters and transforms elements in a single pass
- /reference/array-methods/array-map/ — Transforms each element in the array