Introduction to Enumerables in Ruby
Enumerables are Ruby methods that allow you to transform or otherwise work with collections of objects. If you want to do something to or get answers from an array or a hash, you will be using enumerables.
Each
The basic method enumerables are built from is each. In a sense, every enumerable is just a special case of each. Here’s what an “each” looks like:
example_array = ["what", "who", "chka-chka Slim Shady"]example_array.each do |name|
puts "My name is, #{name}"
end
If we run this, it’ll give us:
My name is, what
My name is, who
My name is, chka-chka Slim Shady
=> ["what", "who", "chka-chka Slim Shady"]
Neat! We can see that we did something with each of the elements of our example_array. Let’s look at the structure of that each statement.
example_array.each
This is the syntax to call the each method on whatever object — in this case, an array named “example_array”.
example_array.each do
end
Enumerables use the do-end format. We’re saying to the computer, “For each element of the example array, do whatever specified thing.” And what is the specified thing?
example_array.each do
puts "My name is, #{name}"
end
For each element of example_array, we want to puts the line “My name is,” followed by the element. We do this by interpolating the element. The remaining piece is just us defining the variable we’re interpolating:
example_array.each do |name|
puts "My name is, #{name}"
end
For those new to enumerables, this is likely the most foreign-looking part. All we’re doing here is saying “For each member of the example array, set that member equal to the variable ‘name’, and then puts ‘My name is, #{name}’.”
Since it’s just a variable, “name” could be anything. We called it “name” to make it clear to us what we’re talking about. This code is exactly the same thing:
example_array.each do |x|
puts "My name is, #{x)"
end
Finally, I want to mention that there is an alternative way to write enumerables. Instead of “do / end”, you can use curly braces. This has the advantage of helping you distinguish your enumerable from whatever other “do / end” type statements that might be near it. We’re going to use that notation in the rest of this article. Here’s what it looks like:
example_array.each{|name|
puts "My name is, #{x}"
}
Or on one line:
example_array.each{|name| puts "My name is, #{x}"}
What else can we do with each? A whole lot! For example, we could use conditionals to check each element and thus find elements meeting certain conditions. We could use a variable to add elements together. We could check if certain statements about the array (or hash) are true. But all all of these things, and many more, can be done more clearly and simply with different enumerable methods.
Introduction to Ruby’s Enumerables
We’re about to start looking at a bunch of different enumerable methods. Your goal here should not be to memorize them — just to look at them and realize the kinds of options you have. As long as you understand the kind of thing you’re looking for, you can always look them up.
All Ruby’s enumerables are listed and detailed in the official Ruby documentation. This page is just meant to be more readable for beginners. We’ll be listing simple cases just to illustrate the logic of each method.
If you scroll up to our each example, you’ll see that while it printed what we told it to, it just returned the original array. If we hadn’t told it to puts anything, it wouldn’t have done anything at all for us.
each is unique in that respect. Every other enumerator is designed to return something specific.
Enumerables that Return a Boolean
There are five enumerable methods that just return “true” or “false”:
- .all?
- .none?
- .one?
- .any?
- .include? / .member?
They’re distinguished by their question marks, which no other enumerator has. Maybe you can start to imagine what each one does from its name. Let’s use an array of numbers to illustrate what these do.
example_array = [1, 2, 3, 4]example_array.all?{|number| number > 0} #=> true
example_array.all?{|number| number > 1} #=> false
example_array.all?{|number| number > 5} #=> falseexample_array.none?{|number| number == 0} #=> true
example_array.none?{|number| number == 1} #=> falseexample_array.one?{|number| number == 1} #=> true
example_array.one?{|number| number > 1} #=> falseexample_array.any?{|number| number < 1} #=> false
example_array.any?{|number| number == 1} #=> true
example_array.any?{|number| number > 1} #=> trueexample_array.include? 2 #=> true
example_array.include? 5 #=> false
example_array.include? "Doggy" #=> false
.include? and .member? are synonyms. They’re simple enough that they don’t need a curly brace statement — just a single object that asks whether it’s included.
In Ruby, everything except false and nil is “truthy”. That can lead us to scenarios like these:
example_array = [1, 2, 3, 4]example_array.all? => true
example_array.none? => false
example_array.one? => false
example_array.any? => trueexample_array = [1, 2, 3, nil]example_array.all? => false
example_array.none? => false
example_array.one? => false
example_array.any? => true
In the above examples we haven’t supplied any kind of block to our methods; they’re just looking at the truthiness of each element in example_array.
Enumerables that Return One of the Elements
These methods traverse your array or hash and return one of the elements of it according to your specifications.
Just as each is the general case of all enumerables, find is the general case of all enumerables that are looking for one of the values in your array or hash. detect is a synonym for find.
Let’s use an array of strings this time.
example_array = ["Trains", "Planes", "Cranes", "Propane", "Automobiles", "etc."]example_array.find{|vehicle| vehicle == "Planes"} #=> "Planes"
example_array.find{|vehicle| vehicle.length == 6} #=> "Trains"
example_array.find{|vehicle| vehicle.length > 6} #=> "Propane"
You might notice that for the latter two queries here, there were multiple elements in example_array that would satisfy the condition. How did find decide which one to return? Well, it just picked the first one it hit. Keep that in mind whenever using these methods.
example_array = ["Trains", "Planes", "Cranes", "Propane", "Automobiles", "etc."]example_array.first #=> "Trains"
example_array.last #=> "etc."
Alright, simple enough.
example_array = ["Trains", "Planes", "Cranes", "Propane", "Automobiles", "etc."]
another_example = [3, 1, 4, 2]example_array.min #=> "Trains"
another_example.min #=> 1example_array.max #=> "etc."
another_example.max #=> 4
min and max behave slightly differently depending on whether they’re looking at numbers or not. If some of the items are numbers and others aren’t, they won’t work at all.
Maybe you were expecting min and max to look at the lengths of the strings. For that — or any other attribute of non-numerical objects — we have min_by and max_by.
example_array = ["Trains", "Planes", "Cranes", "Propane", "Automobiles", "etc."]example_array.min_by{|vehicle| vehicle.length} #=> "etc."example_array.max_by{|vehicle| vehicle.length} #=> "Automobiles"
In the next installment, we’ll look at more complex enumerables.
