Question
Given an array of hashes named @fathers, how can you return only the hashes whose values match a condition?
@fathers = []
@fathers << { "father" => "Bob", "age" => 40 }
@fathers << { "father" => "David", "age" => 32 }
@fathers << { "father" => "Batman", "age" => 50 }
For example, how can you return a new array containing the hashes for fathers whose age is greater than 35? The expected result contains Bob and Batman.
Short Answer
You will learn how to filter an array of hashes in Ruby with Enumerable#select. You will also see how blocks work, how to read hash values safely, and why a comparison such as "age" > 35 must be performed on each hash rather than passed as a standalone argument.
Concept
Array includes Ruby's Enumerable module, which provides methods for searching, filtering, grouping, and transforming collections.
The method used to keep every matching item is select (also called filter). It visits each element in an array and runs the block you provide:
- If the block returns
true, that element is included in the result. - If the block returns
falseornil, that element is left out. selectreturns a new array. It does not change the original array.
In this case, each array element is a hash. The block receives one hash at a time, then reads its "age" value and checks whether it is greater than 35.
This matters because real applications frequently receive collections of records: users from an API, products from a database query, orders from a file, or JSON objects parsed into Ruby hashes. Filtering lets your program work with only the records that meet a rule.
Mental Model
Think of select as a security gate at an event.
Each hash in @fathers approaches the gate one at a time. The block is the gate rule:
“Is this person's age greater than 35?”
If the answer is true, the hash is allowed into a new array. If the answer is false, it is not included. The original line of people is still unchanged.
Syntax and Examples
The basic select syntax is:
matching_items = array.select do |item|
condition_using(item)
end
For an array of hashes with string keys:
older_fathers = @fathers.select do |father|
father["age"] > 35
end
# => [
# { "father" => "Bob", "age" => 40 },
# { "father" => "Batman", "age" => 50 }
# ]
A shorter one-line version is also common:
older_fathers = @fathers.select { |father| father["age"] > 35 }
father is a variable representing one hash during each pass through the array. father["age"] gets the age from that particular hash.
You can filter using other values too:
named_bob = @fathers.select { |father| father["father"] == "Bob" }
# => [{ "father" => "Bob", "age" => 40 }]
For multiple conditions, use && (and) or || (or):
result = @fathers.select do |father|
father["age"] >= 40 && father["father"].start_with?("B")
end
# => Bob and Batman
Step by Step Execution
Consider this code:
fathers = [
{ "father" => "Bob", "age" => 40 },
{ "father" => "David", "age" => 32 },
{ "father" => "Batman", "age" => 50 }
]
older_fathers = fathers.select { |father| father["age"] > 35 }
select evaluates the block once for each hash:
- The first hash is Bob's hash.
father["age"]is40.40 > 35istrue, so Bob's hash is added toolder_fathers. - The second hash is David's hash. Its age is
32.32 > 35isfalse, so David's hash is not added. - The third hash is Batman's hash. Its age is
50.50 > 35istrue, so Batman's hash is added. selectreturns the new array containing the two accepted hashes.
The original fathers array still contains all three hashes.
Real World Use Cases
Filtering arrays of hashes is useful whenever data is represented as records.
- User management: Find users whose account is active or whose age meets a requirement.
- E-commerce: Keep products that are in stock and below a chosen price.
- API responses: Filter parsed JSON records before displaying them.
- Log processing: Keep only entries with an
"error"severity. - Reporting: Select orders created within a date range.
For example, selecting available products:
available_products = products.select do |product|
product["in_stock"] && product["price"] <= 100
end
Real Codebase Usage
In production Ruby code, select is often combined with validation and clear predicate methods.
Filter records with a guard for missing data
If some hashes might not have an age, comparing nil > 35 raises an error. Check first:
older_fathers = fathers.select do |father|
age = father["age"]
age.is_a?(Numeric) && age > 35
end
Put a business rule in a method
A named method makes a complex condition easier to read and test:
def older_than?(person, age_limit)
person["age"].is_a?(Numeric) && person["age"] > age_limit
end
older_fathers = fathers.select { |father| older_than?(father, 35) }
Use find when only one match is needed
If the program needs the first matching record rather than all matching records, use find:
bob = fathers.find { |father| father["father"] == "Bob" }
Avoid unnecessary mutation
Use select when you need a filtered copy. Use select! only when changing the original array is intentional and safe.
Common Mistakes
Comparing the key instead of the value
This does not inspect a hash. It attempts to compare a string to an integer:
# Broken
@fathers.some_method("age" > 35)
Instead, access the age from each hash inside a block:
@fathers.select { |father| father["age"] > 35 }
Forgetting to initialize the array
Using << on an uninitialized instance variable fails because @fathers is nil:
# Broken if @fathers has not been assigned
@fathers << { "father" => "Bob", "age" => 40 }
Initialize it first:
@fathers = []
Using the wrong hash key type
A string key and a symbol key are different in Ruby:
person = { "age" => 40 }
person[:age] # => nil
person["age"] # => 40
Use the same key type that was used when the hash was created.
Expecting select to modify the original array
fathers.select { |father| father["age"] > 35 }
# The returned array was ignored; fathers is unchanged.
Assign the result:
older_fathers = fathers.select { |father| father["age"] > 35 }
Comparing missing or non-numeric ages
# May raise an error if "age" is nil or "unknown"
father["age"] > 35
Validate the value when input may be incomplete:
age = father["age"]
age.is_a?(Numeric) && age > 35
Comparisons
| Method | What it returns | Best use |
|---|---|---|
select / filter | An array of all matching elements | Find every father older than 35 |
find / detect | The first matching element, or nil | Find the first father named Bob |
any? | true or false | Check whether any father is older than 35 |
reject | An array of elements that do not match | Exclude fathers age 35 or younger |
map | An array of transformed values | Extract all father names |
Examples:
fathers.find { |father| father["father"] == "Bob" }
fathers.any? { |father| father["age"] > 35 }
fathers.reject { |father| father["age"] > 35 }
fathers.map { |father| father["father"] }
select and reject return arrays. find stops as soon as it finds one match, so it is usually the better choice when only one result is needed.
Cheat Sheet
# Keep all hashes that match a condition
matches = records.select { |record| record["age"] > 35 }
# Same method; filter is an alias for select
matches = records.filter { |record| record["age"] > 35 }
# Find one matching hash
match = records.find { |record| record["father"] == "Bob" }
# Check whether at least one match exists
exists = records.any? { |record| record["age"] > 35 }
# Exclude matching hashes
others = records.reject { |record| record["age"] > 35 }
# Safely handle a possibly missing age
matches = records.select do |record|
age = record["age"]
age.is_a?(Numeric) && age > 35
end
Key rules:
- Put the condition inside a block:
{ |item| ... }ordo |item| ... end. - Read a hash value with
hash["key"]orhash[:key], matching the key type used in the hash. selectreturns a new array;select!mutates the existing array.- Use
findrather thanselectif you only need one matching item.
FAQ
How do I search an array of hashes in Ruby?
Use select to return every matching hash:
records.select { |record| record["age"] > 35 }
Does Ruby have filter for arrays?
Yes. filter is an alias for select, so both return all elements for which the block is truthy.
How do I return only the first matching hash?
Use find or its alias detect:
records.find { |record| record["father"] == "Bob" }
It returns nil if no hash matches.
Why does "age" > 35 not work in Ruby?
"age" is the key name, represented by a string. You must first retrieve the value from a particular hash, such as father["age"], then compare that value.
Does select change the original array?
No. It returns a new array. select! changes the original array and should be used only when mutation is intended.
Can I filter using more than one hash value?
Yes. Combine conditions with && or ||:
records.select { |record| record["age"] > 35 && record["active"] }
What happens if a hash has no "age" key?
record["age"] returns nil. Comparing nil > 35 raises an error, so validate the value first if missing data is possible.
Mini Project
Description
Build a small Ruby report that filters employee records by age. This demonstrates how an application can validate incoming hash data, select matching records, and then display a useful result.
Goal
Return and print the names of employees older than a chosen age limit.
Requirements
Initialize an array containing at least four employee hashes.
Use string keys named "name" and "age".
Filter employees whose age is greater than a configurable age limit.
Ignore records whose age is missing or is not numeric.
Print each matching employee name and age.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.