Enumerable#select
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:
| Method | Returns elements where block is… | Equivalent to… |
|---|---|---|
select | truthy (matching) | keeping matches |
reject | falsy (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
- /reference/enumerable/enumerable-reject/ — Returns elements for which the block evaluates to false (opposite of select)
- /reference/enumerable/enumerable-find/ — Returns only the first element that satisfies the condition
- /reference/enumerable/enumerable-partition/ — Splits a collection into two arrays based on a condition
- /reference/enumerable/enumerable-filter-map/ — Filters and transforms elements in a single pass