Array#reject

Updated April 1, 2026 · Array Methods
ruby array reject filter stdlib

What does reject do?

reject returns a new array containing all elements for which the block evaluates to falsy. It filters an array by discarding elements that match a condition, keeping the rest.

Unlike find, which returns only the first match, reject collects all non-matching elements. If all elements match the condition, it returns an empty array [].

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

Basic Usage

With a block

[1, 2, 3, 4, 5].reject { |n| n > 2 }   # => [1, 2]
[1, 2, 3, 4, 5].reject { |n| n > 10 }  # => [1, 2, 3, 4, 5]

Without a block

When called without a block, reject returns an Enumerator:

[1, 2, 3].reject  # => #<Enumerator: [1, 2, 3]:reject>

This allows chaining with other methods:

[1, 2, 3, 4, 5].reject.with_index { |n, i| i > 2 }
# => [1, 2, 3]

Filtering Numbers

By numeric condition

temperatures = [12, 15, 8, 22, 19, -3, 25]
cool_days = temperatures.reject { |t| t > 18 }
cool_days  # => [12, 15, 8, -3]

Using symbolic method references

numbers = [1, 2, 3, 4, 5, 6]
odd = numbers.reject(&:even?)
odd  # => [1, 3, 5]

Chaining with map

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

cubes_of_odds = numbers.reject(&:even?).map { |n| n**3 }
cubes_of_odds  # => [1, 27, 125, 343, 729]

Removing by Attribute (Arrays of Hashes)

When an array contains hashes, use the hash key to filter:

users = [
  { name: "Alice", role: "admin", active: true },
  { name: "Bob", role: "editor", active: false },
  { name: "Carol", role: "admin", active: true },
  { name: "Dave", role: "viewer", active: true }
]

inactive = users.reject { |u| u[:active] }
inactive.map { |u| u[:name] }  # => ["Bob"]

non_admins = users.reject { |u| u[:role] == "admin" }
non_admins.map { |u| u[:name] }  # => ["Bob", "Dave"]

Removing with multiple criteria

orders = [
  { id: 1, status: "pending", total: 100 },
  { id: 2, status: "shipped", total: 250 },
  { id: 3, status: "pending", total: 75 },
  { id: 4, status: "delivered", total: 300 }
]

not_pending = orders.reject { |o| o[:status] == "pending" }
not_pending  # => [{ id: 2, status: "shipped", total: 250 }, { id: 4, status: "delivered", total: 300 }]

Removing nil Values

A common use case is removing nil values from an array:

data = [1, nil, 2, nil, 3, "hello", nil]
clean = data.reject(&:nil?)
clean  # => [1, 2, 3, "hello"]

Using compact as an alternative

Ruby also provides compact which removes nil values:

data = [1, nil, 2, nil, 3]
data.compact  # => [1, 2, 3]
data          # => [1, nil, 2, nil, 3]  (original unchanged)

compact only removes nil values, while reject(&:nil?) can be generalised to reject any falsy value:

data = [1, false, 2, nil, 3]
data.reject(&:nil?)    # => [1, false, 2, 3]  (keeps false)
data.reject(&:!)      # => [1, 2, 3]           (removes both false and nil)

reject vs select

reject and select are opposites:

MethodReturns elements where block is…Equivalent to…
selecttruthy (matching)keeping matches
rejectfalsy (non-matching)removing matches
numbers = [1, 2, 3, 4, 5]

numbers.select { |n| n > 3 }    # => [4, 5]   (keep > 3)
numbers.reject { |n| n > 3 }    # => [1, 2, 3] (remove > 3)

Together, they partition a collection:

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

small = numbers.reject    { |n| n >= 3 }
large = numbers.select    { |n| n >= 3 }

small  # => [1, 2]
large  # => [3, 4, 5]

This is equivalent to using partition:

small, large = numbers.partition { |n| n >= 3 }
small  # => [1, 2]
large  # => [3, 4, 5]

Mutating with reject! (Bang Variant)

The bang variant reject! mutates the original array in place instead of returning a new one. If no changes are made, it returns nil.

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

result = numbers.reject! { |n| n > 2 }

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

When reject! returns nil

If the block condition matches nothing (no change needed), reject! returns nil:

numbers = [1, 2, 3]

result = numbers.reject! { |n| n > 10 }
result  # => nil  (no changes made, numbers stays [1, 2, 3])

Comparing reject vs reject!

original = [1, 2, 3, 4, 5]

# reject returns a new array, original unchanged
filtered = original.reject { |n| n > 2 }
original   # => [1, 2, 3, 4, 5]
filtered   # => [1, 2]

# reject! modifies the original
mutated = original.reject! { |n| n > 2 }
original   # => [1, 2]
mutated    # => [1, 2]
mutated.equal?(original)  # => true

Use reject! when you want to reuse the same variable rather than allocating a new array.

Performance Considerations

reject always iterates through the entire collection — it cannot short-circuit like find or any?. Time complexity is O(n) regardless of when matches are found.

# Always checks all elements, even if first one matches
[1, 2, 3, 4, 5].reject { |n| n > 1 }
# Checks: 1, 2, 3, 4, 5 (all five iterations)

Memory usage

reject creates a new array containing all non-matching elements. For very large collections with few non-matches, consider lazy iteration:

# Eager: builds full array first
result = (1..1_000_000).to_a.reject { |n| n.odd? }

# Lazy: yields matches one at a time
result = (1..1_000_000).lazy.reject { |n| n.odd? }
# then take what you need: result.first(10)

Bang variant performance

reject! avoids allocating a new array, which can matter for large arrays. However, it still iterates through all elements — the performance difference is solely in memory allocation, not iteration cost.

Practical Examples

Filtering records

products = [
  { name: "Widget", price: 100, discontinued: false },
  { name: "Gadget", price: 200, discontinued: true },
  { name: "Doodad", price: 50, discontinued: false },
  { name: "Thingamajig", price: 75, discontinued: true }
]

active_products = products.reject { |p| p[:discontinued] }
active_products.map { |p| p[:name] }  # => ["Widget", "Doodad"]

Cleaning user input

raw_input = ["alice", "", "bob", "   ", "carol", nil, "dave"]

# Remove blank and nil strings
clean = raw_input.reject { |s| s.nil? || s.strip.empty? }
clean  # => ["alice", "bob", "carol", "dave"]

Removing outliers

scores = [45, 78, 92, 55, 88, 33, 67, 200, 150]

# Remove unusually high scores
reasonable = scores.reject { |s| s > 100 }
reasonable  # => [45, 78, 92, 55, 88, 33, 67]

Combining with sum

transactions = [
  { description: "Salary", amount: 3000 },
  { description: "Rent", amount: -1000 },
  { description: "Groceries", amount: -150 },
  { description: "Bonus", amount: 500 }
]

total = transactions.reject { |t| t[:amount] < 0 }.sum { |t| t[:amount] }
total  # => 3500

See Also