Enumerable#select

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

What does select do?

select returns an array containing all elements for which the block evaluates to truthy. It filters a collection based on a condition — keeping elements that match, discarding the rest.

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

Basic Usage

With a block

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

With a predicate argument

You can pass a pattern object instead of a block. The method calls === on the pattern for each element:

[1, 2, 3, 4].select(Integer)   # => [1, 2, 3, 4]
[1, 2, 3, 4].select(String)     # => []

words = ["apple", "banana", "cherry", "apricot"]
words.select(/^a/)              # => ["apple", "apricot"]

This is equivalent to:

[1, 2, 3, 4].select { |n| Integer === n }  # => [1, 2, 3, 4]

Without a block

When called without a block or argument, select returns an Enumerator:

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

This allows chaining with other methods:

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

Filtering Arrays

By numeric condition

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

Using symbolic method references

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

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

Chaining with other Enumerable methods

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  .select { |n| n.even? }
  .map { |n| n * 2 }
# => [4, 8, 12, 16, 20]

Filtering Hashes

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

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

adults = ages.select { |name, age| age >= 18 }
adults  # => { alice: 30, charlie: 25 }

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

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 }
}

available = products.select { |_, info| info[:in_stock] }
available.keys  # => [:widget, :doodad]

expensive = products.select { |_, info| info[:price] > 75 }
expensive.keys  # => [:widget, :gadget]

find_all: The Alias

select and find_all are identical — find_all is simply an alias:

[1, 2, 3, 4].select      { |n| n > 2 }  # => [3, 4]
[1, 2, 3, 4].find_all    { |n| n > 2 }  # => [3, 4]

Both methods share the same implementation, return type, and behaviour. Use whichever reads more naturally in your context. select is more common in Ruby code; find_all may read better when you want to emphasise “finding all” matches rather than “selecting by” a condition.

# These are functionally identical:
results.select { |item| item.active? }
results.find_all { |item| item.active? }

select vs reject

select and reject 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]

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]

Inverting a filter

You can also use reject to achieve the inverse of select:

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

numbers.select  { |n| n.odd? }   # => [1, 3, 5]
numbers.reject  { |n| n.even? }   # => [1, 3, 5] (same result)

Performance Considerations

select always iterates through the entire collection — it cannot short-circuit like find or any?. The 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].select { |n| n > 1 }
# Checks: 1, 2, 3, 4, 5 (all five iterations)

Memory usage

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

# Eager: builds full array first
result = (1..1_000_000).select { |n| n.prime? }

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

Practical Examples

Filtering user 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: true }
]

admins = users.select { |u| u[:role] == "admin" && u[:active] }
admins.map { |u| u[:name] }  # => ["Alice", "Carol"]

Finding matching file paths

require "pathname"

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

Grouping by a condition

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

passed = scores.select { |s| s >= 60 }
failed = scores.reject { |s| s >= 60 }

passed  # => [78, 92, 88, 67]
failed  # => [45, 55, 33]

Filtering 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 }
]

# Pending orders over $80
high_pending = orders.select do |o|
  o[:status] == "pending" && o[:total] > 80
end
high_pending  # => [{ id: 1, status: "pending", total: 100 }]

Transforming after filtering

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

squares_of_evens = numbers.select(&:even?).map { |n| n**2 }
squares_of_evens  # => [4, 16, 36, 64, 100]

With sum or count

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

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

income    # => 3500
expenses  # => -1150

See Also