Enumerable#reject

Updated April 1, 2026 · Enumerable
ruby enumerable reject filter stdlib

What does reject do?

reject returns an array containing all elements for which the block evaluates to falsy. It filters a collection by removing elements that match a condition — keeping everything that does not match.

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

reject is the logical opposite of select — where select keeps matches, reject discards them.

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 or argument, 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 Arrays

By numeric condition

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

Using symbolic method references

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

words = ["cat", "dog", "elephant", "bee"]
short_words = words.reject { |w| w.length > 3 }
short_words  # => ["cat", "dog", "bee"]

Chaining with other Enumerable methods

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  .reject { |n| n.even? }
  .map { |n| n * 2 }
# => [2, 6, 10, 14, 18]

Filtering Hashes

When called on a hash, reject passes each key-value pair to the block:

ages = { alice: 30, bob: 17, charlie: 25, diana: 16 }

minors = ages.reject { |name, age| age >= 18 }
minors  # => { bob: 17, diana: 16 }

# Removing entries where the key matches a pattern
config = { host: "localhost", port: 5432, ssl: false, debug: true }
non_debug = config.reject { |k, v| k.to_s.include?("ssl") || k == :debug }
non_debug  # => { host: "localhost", port: 5432 }

Using hash value as filter criterion

products = {
  widget: { price: 100, in_stock: true },
  gadget: { price: 200, in_stock: false },
  doodad: { price: 50, in_stock: true }
}

out_of_stock = products.reject { |_, info| info[:in_stock] }
out_of_stock.keys  # => [:gadget]

cheap = products.reject { |_, info| info[:price] > 75 }
cheap.keys  # => [:doodad]

reject vs select

reject and select are complementary — they partition a collection based on the inverse condition:

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]

passing = numbers.select    { |n| n >= 3 }
failing = numbers.reject    { |n| n >= 3 }

passing  # => [3, 4, 5]
failing  # => [1, 2]

This is equivalent to using partition:

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

When to use reject instead of select

Prefer reject when:

  • You want to express what to remove rather than what to keep
  • The removal condition is simpler or more natural to express
  • You’re filtering out error cases or invalid entries
# select reads well when keeping matches
valid_users = users.select { |u| u.active? && u.verified? }

# reject reads well when removing invalid entries
invalid_users = users.reject { |u| u.active? && u.verified? }

Performance Considerations

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

# Always checks all elements, even if first one does not match
[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 many matches, consider lazy iteration:

# Eager: builds full array first
result = (1..1_000_000).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)

Practical Examples

Filtering out invalid records

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

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

Excluding certain file types

require "pathname"

files = Pathname.new(".")
ruby_files = files.children.reject { |p| p.file? && p.extname != ".rb" }
ruby_files  # => [#<Pathname:app.rb>, #<Pathname:config.rb>, ...]

Removing nil or falsy values

values = [1, nil, 2, false, 3, nil, 4]
clean = values.reject(&:nil?)
clean  # => [1, 2, false, 3, 4]

# To also remove falsy values:
clean = values.reject { |v| !v }
clean  # => [1, 2, 3, 4]

Chaining filter operations

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

# First remove odds, then keep only numbers greater than 4
result = numbers
  .reject(&:odd?)
  .reject { |n| n <= 4 }

result  # => [6, 8, 10]

# Equivalent using select:
result = numbers
  .select(&:even?)
  .select { |n| n > 4 }

result  # => [6, 8, 10]

With sum or count

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

expenses = transactions.reject { |t| t[:amount] > 0 }
expense_total = expenses.sum { |t| t[:amount].abs }

expense_total  # => 1150

Cleaning up configuration

env = {
  RAILS_ENV: "production",
  DEBUG: "false",
  DATABASE_URL: "postgres://localhost/myapp",
  LOG_LEVEL: "info"
}

# Remove empty string and "false" config values
clean_env = env.reject { |_, v| v == "" || v == "false" }
clean_env  # => { RAILS_ENV: "production", DATABASE_URL: "postgres://localhost/myapp", LOG_LEVEL: "info" }

See Also