39

The Enumerable module in Ruby: Part I

 5 years ago
source link: https://www.tuicool.com/articles/hit/6NvuYne
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
EJbUBzm.jpg!webaiyqArj.jpg!web
benjamins.to_enum

The Enumerable module in Ruby: Part I

In this article we’re going to explore the following topics:

Enumerable
Enumerator

Introduction

As Ruby is a fully object-oriented programming language, even traversing a collection can be accomplished directly through the collection itself.

This logic is encapsulated in the Enumerable module.

The Enumerable module

When the Enumerable module is included in a class then a bunch of:

  • Traversal methods
  • Searching methods
  • Sorting methods

are added to the including class.

This module is widely used among the most popular Ruby gems and projects— for example Ruby on Rails , devise , etc..

Also few important Ruby classes include this module, such as the Array , Hash , Range classes.

Let’s have an overview of the provided API for this module.

Let’s have a look to a traversal, a sorting and a searching method

vmAz6rn.jpg!web

Firstly, we use the well-known map method.

Then we use the sort method, which sorts by ascendent alphabet by default.

Finally, we use the searching method first to fetch the first item of the array.

Including the Enumerable module

A class that includes the Enumerable module must respond to the each method:

vmAz6rn.jpg!web

In the above example, a NoMethodError is raised due to the fact that, internally, the Enumerable#map method makes a call to the each method — which is not defined by the Users class.

So, let’s define it

vmAz6rn.jpg!web

Here we define a Users#each method that loops through the @users array — which is the data source of the Users class — then we yield each value of the @users array.

As the Users#each method is defined, then the Enumerable#map method can use it and get each user as argument of its block.

Feel free to read The yield keyword article if you are unfamiliar with the yield keyword.

Now, that we are more familiar with the Enumerable module, let’s dig into the Enumerator class.

The Enumerator class

An Enumerator is a data source, that is consumed by the Enumerable methods and can also be used for external iteration.

How to use an Enumerator?

vmAz6rn.jpg!web

If no argument is passed to map (and to almost all of the methods of the Enumerable module) then an instance of the Enumerator class is returned.

This enumerator is linked to the [1, 2, 3] array (data source) and the map method (data consumer)

vmAz6rn.jpg!web

In the above example, the map data consumer is executed in the context of the each method.

As you can you see, the enumerator executes only once the content of the block — as there is only 3 calls to the puts method.

This use case is not very efficient as we would prefer to call the map method as following [1, 2, 3].map { |n| puts n; n + 2 } .

Ok.. but here is a better one

vmAz6rn.jpg!web

As the map_with_index is not yet part of the Enumerable module, then a cool way to copy the behaviour of this method is by using the Enumerator returned by the map call without argument and then calling the Enumerator#with_index method.

So, the map data consumer is executed in the context of the with_index method.

Chaining Enumerators

The Ruby language gives you the possibility to chain Enumerator s

vmAz6rn.jpg!web

Here, the map data consumer is executed in the context of the with_index data consumer which is linked to the context of the each method.

External iteration

An iteration is called internal when the iteration logic is encapsulated in the method.

For example, The Array#each method.

In contrary, an iteration is called external when the iteration logic is defined outside of a method.

Let’s see how the Enumerator module handles the external iteration

vmAz6rn.jpg!web

The Kernel#to_enum returns an instance of the Enumerator class with self (the [1,2,3] array in this case) as data source and the each method as default data consumer.

Moving the internal cursor

vmAz6rn.jpg!web

The Enumerator class provides a bunch of methods to manipulate an internal cursor that keeps the state of the external iteration.

vmAz6rn.jpg!web

The Enumerator#peek method returns the value contained at the cursor position.

The Enumerable#next method moves the cursor to the next position.

The Enumerator#next raises a StopIteration error when it is called and the cursor is at the last position of the data source.

The Enumerable#rewind method moves the cursor to the first position.

Voilà !

Feel free to have a look to the Part II .

May I have your attention please :microphone::microphone:

m6rQNfj.png!webVZ7BRr6.png!web

Feel free to subscribe here: www.rubycademy.com

Thank you for taking the time to read this post :-)

Feel free to :clap: and share this article if it has been useful for you. :rocket:

Here is a link to my previous article: Numbers and Class Hierarchy in Ruby .


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK