Ruby enumerables explained

An enumerable can be a key component to data manipulation and the first part to knowing how to use them is knowing what the basic structure looks like. There are several parts to the basic structure that are required to perform the enumerable.

First, we need data to perform the enumerable. The collection object is a data type, holding elements or a set of elements. Some examples of a collection object are an array and a hash. These collections have a set of data that an enumerable will loop over one by one. For an array, the elements are separated by a comma. We will focus on arrays for now because they’re pretty straightforward when it comes to using an enumerable.

“letters” is the name of the array(collection object). Each letter(“a” - “g”) in the array, is an element in the array.

Enumerables are methods built into Ruby so they can easily be accessed by just calling it on a collection object. Think of a collection object as a jigsaw puzzle, and the elements of the collection object as puzzle pieces. A jigsaw puzzle is made from an arbitrary amount of puzzle pieces, and each of those puzzle pieces make up that jigsaw puzzle. The enumerable method you choose performs the data manipulation on the collection object. If we wanted to call an enumerable on the jigsaw puzzle, it means we’re trying to do something to each of those puzzle pieces. Hypothetically, if “enlarge’ was an enumerable, we could call “enlarge” on the jigsaw puzzle and it would enlarge every puzzle piece one by one.

#map enumerable being called on the “letters” array
enumerable #map (sometimes called an iterator)

In the code snippet above, the enumerable #map is being called on the “letters” array. The “.” between the “letters” and #map signifies that a method is being called on the collection object. #map iterates over the elements and performs whatever you ask of it in the code block, and its a pretty universal tool. We’ll come back to the specific types of enumerables and what they do. Let’s move on to the next piece of the basic structure, the “do” and “end”.

“do” and “end” being used to determine block

The “do” is where the block of code being performed begins. The “end” is where the block of code ends. Everything in between the “do” and the “end” is basically what you ask the enumerable to do to each element in the collection object. The “do” and “end” in Ruby are also defined as brackets and are interchangeably used.

same enumerable being performed with brackets instead of a “do” and “end”

The next piece is the red “letter” between the two pipes. It’s called a block variable and its a reference to the element that the enumerable is iterating over.

Going back to the jigsaw puzzle example, we referred to the elements in the array as puzzle pieces. Now it doesn’t matter what color or shape the puzzle pieces are, they are still referred to as puzzle pieces. We see the elements in the “letters” array as “a”, “b”, “c”, and so on. But a ruby enumerable loops over one element at a time, so it needs a general reference for each element so it can understand what code you are asking it to perform on the elements in the collection object.

Between the “do” and the “end” is where the block of code being run by the enumerable. The block variable is the name you give the elements in the collection object every time the enumerable loops over to look at the next element. In the picture above, I named the block variable “letter” for simplicity sake, but it really doesn’t matter what you name it.

block variable naming is non-discriminate

The last piece of the basic structure is between the block variable and the end. This is where you write the code you want performed so the enumerable can execute it. Simply put, the block is the where you assign the parameters for the code you want to run. In the example above, I’m asking #map to loop over each element and capitalize it. The return of the code being performed is:

Return after #map was performed

But when I call “letters” again after the code was performed, ruby gives me this:

original array was unchanged after #map performed

So what gives? Well, #map returns a new array filled with whatever gets returned by the block each time it runs. But it doesn’t permanently change the array, so if you call the collection object again, it will return unchanged.

Another enumerable is #find_all. It returns a new collection object (array) filled with only those original items where the block you gave it returned true. This is really useful for when you need to find all the data that matches certain conditions you set.

#find_all with #inlcude? as the condition
Ruby returned after the #find_all enumerable was run

In the example above, I’m asking #find_all to loop over the “words” array to find any element that has “ight” in it. #find_all returns only items that are true, but if it finds no matches, it will return an empty array. #find is very similar to #find_all, but it will return the first item that matches, not a collection of items. In the example above, #find would return just “right”.

Another pair of enumerables I find useful is #min_by and #max_by. #min_by will return the minimum value for the condition(s) you set. #max_by will return the maximum value for the condition(s) you set. These are useful if you need to find minimum or maximum values dependent on certain conditions.

Can you guess what the conditions are and why Ruby returned those values? There are so many intricacies to ruby methods, there’s always something new to learn about their behavior.

Enumerables are extremely useful tools when working with collection objects, and there are a variety of enumerables available to perform various tasks. There are plenty of resources on which enumerables are available and how to use them, and ruby-docs.org is a great place to start.

“If you ever talk to a great programmer, you’ll find they know their tools like an artist knows their paintbrushes.” — Bill Gates