Question

Search an Array of Hashes by Value in Ruby with select

rubysearchhasharrays

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 false or nil, that element is left out.
  • select returns 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.

Take Quiz

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:

  1. The first hash is Bob's hash. father["age"] is 40. 40 > 35 is true, so Bob's hash is added to older_fathers.
  2. The second hash is David's hash. Its age is 32. 32 > 35 is false, so David's hash is not added.
  3. The third hash is Batman's hash. Its age is 50. 50 > 35 is true, so Batman's hash is added.
  4. select returns 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
Take Quiz

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

MethodWhat it returnsBest use
select / filterAn array of all matching elementsFind every father older than 35
find / detectThe first matching element, or nilFind the first father named Bob
any?true or falseCheck whether any father is older than 35
rejectAn array of elements that do not matchExclude fathers age 35 or younger
mapAn array of transformed valuesExtract 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.

Take Quiz

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| ... } or do |item| ... end.
  • Read a hash value with hash["key"] or hash[:key], matching the key type used in the hash.
  • select returns a new array; select! mutates the existing array.
  • Use find rather than select if 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more