Hash#reject

hash.reject { |key, value| block } -> hash
Returns: Hash · Updated April 3, 2026 · Hash Methods
hash stdlib

Hash#reject returns a new hash containing only the key-value pairs where the block returns falsy. It is the inverse of Hash#select — where select keeps pairs that pass the condition, reject discards them. The original hash is never modified by the non-bang version.

Syntax

hash.reject { |key, value| condition }  # => new hash
hash.reject                             # => Enumerator
hash.reject! { |key, value| condition }  # => self or nil
hash.reject!                            # => Enumerator

Both methods return an Enumerator when called without a block, so you can chain other iterators or pass the iterator to lazy.

Hash#reject vs Hash#reject!

The bang/non-bang distinction follows Ruby’s standard pattern:

MethodModifies original?Returns when changedReturns when nothing removed
rejectNoNew hashNew hash (copy)
reject!Yesselfnil
h = { a: 1, b: 2, c: 3 }

new_h = h.reject { |k, v| v > 1 }
puts h      # => {:a=>1, :b=>2, :c=>3}  (unchanged)
puts new_h  # => {:a=>1}

h.reject! { |k, v| v > 1 }
puts h      # => {:a=>1}  (modified in place)

That nil return from reject! trips people up. When nothing gets removed, you get nil instead of the hash or self:

h = { a: 1, b: 2 }
result = h.reject! { |k, v| v > 10 }
result  # => nil  (nothing removed — returns nil, not h)
h       # => {:a=>1, :b=>2}  (unchanged)

result = h.reject! { |k, v| v > 0 }
result  # => {}  (something removed — returns self)
h       # => {}

This means you can’t safely chain reject! the way you chain reject. After h.reject! { ... } you might have nil or the now-empty hash — neither is a hash you can call hash methods on predictably.

reject is NOT delete_if

Ruby documents delete_if as the equivalent of reject!, but they are separate C implementations, not aliases. They produce the same result, but delete_if always returns self, while reject! can return nil:

h = { a: 1, b: 2, c: 3 }

# delete_if: always returns self
h.dup.delete_if { |k, v| v > 2 }  # => {:a=>1, :b=>2}

# reject!: returns nil if nothing removed
h.dup.reject! { |k, v| v > 10 }   # => nil
h.dup.reject! { |k, v| v > 2 }    # => {:a=>1, :b=>2}

Both remove entries where the block returns truthy. The only behavioral difference is that reject! signals “no changes” with nil.

reject vs select (and filter)

select and filter are identical — filter is the newer name. They keep pairs where the block returns truthy. reject is the exact inverse:

MethodKeeps pair when block returns…Alias
select / filterTruthyYes (filter = select)
rejectFalsyNo
h = { a: 1, b: 2, c: 3, d: 4 }

h.select  { |k, v| v > 2 }  # => {:c=>3, :d=>4}
h.reject  { |k, v| v > 2 }  # => {:a=>1, :b=>2}
h.filter  { |k, v| v > 2 }  # => {:c=>3, :d=>4}  (filter == select)

reject { |k,v| condition } gives the same result as select { |k,v| !condition }. The bang variants follow the same symmetry:

h = { a: 1, b: 2, c: 3, d: 4 }
h.select!  { |k, v| v > 2 }  # => {:c=>3, :d=>4}
h.filter!  { |k, v| v > 2 }  # => {:c=>3, :d=>4}  (same thing)
h.reject!  { |k, v| v > 2 }  # => {:a=>1, :b=>2}

select! and filter! are true aliases in the C source. reject! is its own implementation.

Enumerator Form

Without a block, both return an Enumerator:

h = { a: 1, b: 2, c: 3 }

h.reject.each { |k, v| v.even? }   # => {:a=>1, :c=>3}
h.reject!.each { |k, v| v.even? } # => {:a=>1, :c=>3}

This lets you pass the Enumerator to lazy or chain other iterator methods.

Common Patterns

Cleaning nil values from user input

input = { name: "Alice", email: "alice@example.com", phone: nil, age: nil }

input.reject { |_k, v| v.nil? }
# => {:name=>"Alice", :email=>"alice@example.com"}

Removing entries by key pattern

env = { RAILS_ENV: "production", RACK_ENV: "production", DEBUG: "true", HOME: "/root" }

env.reject { |key, _| key.to_s.start_with?("RAILS") }
# => {:DEBUG=>"true", :HOME=>"/root"}

Chaining filters

data = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }

data.select { |k, v| v > 2 }.reject { |k, v| v.even? }
# => {:c=>3, :e=>5}

Edge Cases

Empty hash: reject returns {}. reject! returns nil.

All pairs rejected: reject returns {}. reject! returns self (the now-empty hash).

Default values are not copied to the new hash:

h = Hash.new(0)
h[:missing]  # => 0

h.reject { |k, v| v > 10 }
h[:missing]  # => 0  (original unchanged)

The default value or proc is not copied to the returned hash by reject. The new hash has nil defaults.

See Also