What is Enumerable?
Enumerable is a collection of iteration methods, a Ruby module, and a big part of what makes Ruby a great programming language.
Enumerable includes helpful methods like:
map
select
inject
Enumerable methods work by giving them a block.
In that block you tell them what you want to do with every element.
For example:
[1,2,3].map { |n| n * 2 }
Gives you a new array where every number has been doubled.
Exactly what happens depends on what method you use, map
helps you transform all the values, select
lets you filter a list & inject
can be used to add up all the values inside an array.
There are over 20 Ruby Enumerable methods.
Let’s study one in detail.
The Each_Cons Method
My new favorite Enumerable
method is each_cons
!
Here’s why:
This method is really useful, you can use it to find n-grams or to check if a sequence of numbers is contiguous when combined with all?
, another Enumerable
method.
each_cons
gives you sub-arrays of size n
, so if you have [1,2,3]
, then each_cons(2)
will give you [[1,2], [2,3]]
.
Let’s see an example:
numbers = [3,5,4,2] numbers.sort.each_cons(2).all? { |x,y| x == y - 1 }
This code starts by sorting the numbers, then calling each_cons(2)
, which returns an Enumerator
object, and then it calls the all?
method to check if all the elements match the condition.
Here is another example, where I use each_cons
to check if a character is surrounded by the same character, like this: xyx
.
str = 'abcxyx' str.chars.each_cons(3).any? { |a,b,c| a == c }
There is more!
If you wanted to know how many times this pattern occurs, instead of getting a true
/ false
result, you can just change any?
to count
.
What I find even more fascinating is the implementation for the each_cons
method.
array = [] each do |element| array << element array.shift if array.size > n yield array.dup if array.size == n end
Note: This comes from the Rubinius implementation of
Enumerable
. You can find the original source code here.
The implementation starts with an empty Ruby array, then it iterates through the elements using each
.
Everything is pretty standard until here. But then it adds the element to the array and it trims the array (using Array#shift) if the size is bigger than what we want.
The size is the argument to each_cons
.
Then it yields a dup
of the array if the array has the requested size.
I think this is genius, because it keeps a ‘sliding window’ sort of effect into our enumerable object, instead of having to mess around with array indexes.
More Fascinating Methods
Method | Description |
---|---|
count | Exactly what the name says, count things that evaluate to true inside a block |
group_by | Group enumerable elements by the block return value. Returns a hash |
partition | Partition into two groups. Returns a two-dimensional array |
any? | Returns true if the block returns true for ANY elements yielded to it |
all? | Returns true if the block returns true for ALL elements yielded to it |
none? | Opposite of all? |
cycle(n) | Repeat ALL the elements n times, so if you do [1,2].cycle(2) you would have [1,2,1,2] |
find | Like select , but it returns the first thing it finds |
inject | Accumulates the result of the previous block value & passes it into the next one. Useful for adding up totals |
zip | Glues together two enumerable objects, so you can work with them in parallel. Useful for comparing elements & for generating hashes |
map | Transforms every element of the enumerable object & returns the new version as an array |
Wrapping Up
As you have seen, Enumerable
is a module that is worth mastering, so hop over to the documentation and see what it can do for you!
Don’t forget to share & subscribe to my newsletter in the form below if you enjoyed this article. It would help me a lot! 🙂
Really helpful article, thank you so much for sharing it, Jesus!!!
Thank you for reading 🙂
Thank you for sharing article. I will read the documentation.
There are lots of interesting methods there. Check out the
partition
method 🙂