Arrays are one of the most useful data structures in Ruby. They allow you to store a collection of elements in a single variable. Once you have data in an array, manipulating and accessing those elements is easy.
In this comprehensive guide, we will explore the various methods Ruby provides to find or filter elements stored in an array.
Overview of Arrays in Ruby
Before we jump into the different techniques, let‘s do a quick overview of arrays in Ruby.
Arrays are ordered, integer-indexed collections of any object. You can create them by enclosing elements in square brackets.
numbers = [1, 2, 3, 4, 5]
This array contains integers. But arrays can store any Ruby objects like strings, other arrays, hashes etc.
mixed = ["Hello", 10, [1,2], {name: "John"}]
The index starts from 0. So in the numbers array, 1 is at index 0, 2 is at index 1 and so on.
You can access elements by referring to the index.
numbers[0] # => 1
numbers[2] # => 3
This makes arrays perfect for storing related pieces of information that you want to iterate over.
Now let‘s look at different ways to find elements inside an array.
1. Using the include? Method
The include? method allows you to check if a particular value exists in the array.
numbers = [1, 2, 3, 4, 5]
numbers.include?(3) # => true
numbers.include?(10) # => false
include? returns a boolean indicating if the element exists. This makes it useful for presence checks.
numbers.include?(2) ? puts "2 is present" : puts "2 not found"
You can also pass ranges to look for.
numbers.include?(1..3) # true
One limitation of include? is that it uses the == operator to compare. So for complex objects, you need to override the == method.
Overall, include? gives an easy way to check if a value is part of the array.
2. Using the find method
The find method allows you to pass a code block that runs through each element checking for a condition. It returns the first element for which the block returns a truthy value.
numbers = [1, 2, 3, 4, 5, 6]
even = numbers.find { |n| n.even? } # => 2
Here we pass a block to check if a number is even. Find executes the block for each element (with the element as n) and returns the first even number i.e. 2.
If no element matches, it returns nil.
numbers.find { |n| n > 10 } # => nil
You can pass complex conditions in the block to pattern match elements you want.
3. Using the select Method
While find returns the first matching element, select returns all elements for which the block returns true.
numbers = [1, 2, 3, 4, 5, 6]
evens = numbers.select { |n| n.even? } # => [2, 4, 6]
select is useful when you want all elements that meet a criteria.
You can call methods on the elements within the block.
names = ["john", "sarah", "alex"]
long_names = names.select { |name| name.length > 4} # => ["sarah", "alex"]
The bang version select! performs the filtering in-place.
4. Using the filter Method
filter is an alias for select. So both behave the same way.
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = numbers.filter { |n| n.even? } # [2, 4, 6]
When chaining methods, some developers prefer filter over select as it reads better.
numbers
.filter { |n| n.even? }
.map { |n| n * 2 }
So feel free to use filter instead of select.
5. Using the find_all Method
find_all is similar to select, but there is a subtle diference in how the methods handle nil.
select treats nil as a falsy value. So elements for which the block returns nil are omitted from the result.
[1, 2, nil, 3].select { |e| e } # => [1, 2, 3]
On the other hand, find_all treats nil as a truthy value and includes such elements in the output.
[1, 2, nil, 3].find_all { |e| e } # => [1, 2, nil, 3]
So when working with arrays that contain nil values, find_all includes them in the output even if nil while select skips them.
6. Using the grep Method
While the above methods filter based on a block, grep allows you to filter elements based on a pattern.
colors = ["red", "green", "blue", "yellow"]
colors.grep(/re/) # => ["red"]
You pass a regular expression pattern or a string/number to grep. It returns all elements that match the pattern.
Some examples:
[1, 2, 3, 4].grep(2) # => [2]
colors.grep("red") # => ["red"]
names.grep(/^al/i) # => ["Alex"] - case insensitive match
You can also pass a block to further filter the matches.
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.grep(2..4) { |n| n.even? } # [2, 4]
This selects elements between 2 and 4 that are even.
The bang version grep! performs the grep filtering inplace.
7. Using the where Method
If you use Rails, you would be familiar with the where method used by ActiveRecord.
Ruby Arrays get the similar where method from ActiveSupport.
You can pass conditions to filter elements, like in SQL.
products = [
{ name: "Shirt", price: 20 },
{ name: "Pants", price: 35 },
{ name: "Shoes", price: 50 }
]
cheap = products.where(price: 1..30)
# => [{:name=>"Shirt", :price=>20 }]
where takes a hash of conditions and selects elements that match.
Some more examples:
products.where(name: ‘Shirt‘)
products.where([‘price < ?‘, 40]) # equivalent to price: 1..39
You can pass ranges, arrays, regexes, strings etc. in the hash to match elements.
8. Using the index Method
While the above methods find elements based on certain criteria, you sometimes want to directly access an element based on its position in the array.
The index method lets you find the index of an element.
numbers = [1, 3, 5, 7, 9]
index = numbers.index(5) # => 2
numbers[index] # => 5
This returns the index of the first element matching the passed value.
You can then use the index to fetch elements at that position.
index returns nil if no matches found.
An important point about index is that it uses the == match to compare elements. So be wary while using index with complex elements.
9. Using the find_index Method
find_index works similar to index, but it accepts a block to test for a match instead of an element.
The block gets executed against each element like in find. It returns the index of first element where the block returns a truthy value.
numbers = [1, 3, 5, 7, 9]
numbers.find_index { |n| n.odd? } # => 0
This returns the index of first odd number.
Some more examples:
numbers.find_index { |n| n > 5 } # => 2
products.find_index { |p| p[:price] > 40 } # => 2
So find_index gives more flexibility than index to test for a match.
10. Using the Index By Method
While methods like index and find_index return a numeric index, index_by allows you to index the array elements by any attribute.
For example, if you have an array of products, you may want to index them based on the product name to facilitate easy lookup.
index_by takes a block that returns the indexing attribute per element. This attribute is then used to index the elements.
products = [
{ name: "Shirt", price: 20 },
{ name: "Pants", price: 35 },
{ name: "Shoes", price: 50 }
]
indexed = products.index_by { |product| product[:name] }
# {
# "Shirt" => {:name=>"Shirt", :price=>20},
# "Pants" => {:name=>"Pants", :price=>35},
# "Shoes" => {:name=>"Shoes", :price=>50}
# }
Now you can access elements by name attribute.
indexed["Shirt"] # => {:name=>"Shirt", :price=>20}
indexed["Pants"] # => {:name=>"Pants", :price=>35}
This provides a flexible way to index elements to make access easy, without using numeric indices.
Conclusion
Ruby ships with a lot of built-in array methods that allow you to search and filter data with ease.
Whether you want to check for a value or index elements in a certain way, there is likely a array method that does it. Mastering these methods leads to efficient and readable code for array processing.
I hope this guide gives you a good overview of how to find elements within arrays in Ruby. Each method caters to a different use case.
So choose the right tool for your task and leverage Ruby‘s powerful array methods for effective array programming.


