Enumerator: When to Use and Why are they so special?

Enumerator is used for iterating over a collection of items. It allows both internal and external iteration.

So how do we Create an Enumerator?

There are 3 different ways to create it. They are from,
  • A Block,
  • An Enumerable,
  • A Blockless Enumerable Method Call.
Let’s have a look on each of the method now.

From a Block

We can create an enum by passing a block to its constructor. A yielder object will be passed to this block. The yielder’s #<< method can be used to define the elements. Enumerator#next can then be used to provide iteration. Eg:
enum = Enumerator.new do |yielder|
  yielder << "Red"
  yielder << "Panthers"
enum.next # "Red"
# StopIteration: when it reached an end

From an Enumerable

The most common way to create an Enumerator is from an Enumerable object, specifically, an object that defines a #each method. Object#to_enum is implemented to return a new Enumerator which will enumerate by sending #each to its receiver. Eg:
array = [1, 2, 3]
enum = array.to_enum
enum.each {|n| n * 2} # 2 4 6

From a Blockless Enumerable Method Call

There are several Enumerable methods that take a block and returns an Enumerator when called without a block. For instance, calling Array#select without a block will return an Enumerator with an #each method that will filter like #select.These blockless calls offer a concise alternative to Object#to_enum. Eg:
array = [1, 2, 3, 4]
enum = array.select
enum.each {|n| n % 2 == 0} # [2, 4]

When do we use an Enumerator?

We can use this type instead of defining many constants for fields (such as a status).For example, status can have values active and inactive in different condition. So that, we can use this type as below: Eg:
status =%w(active inactive).each
puts status.class # => Enumerator
puts status.next # => active
puts status.next # => inactive
puts status.next # raises StopIteration
So by using it, we can simplify our code as above.

Why are they so special?

  • It provides enumeration functionality to objects without it.
  • You can control the iteration process in an efficient way using different methods like .each, .select, .next, .peek etc.
  • There is a feature called Lazy enumerator, which will prevent iterate over an infinite collection.
  • It saves a lot of typing(see above example), which makes the programmer more efficient.
So that’s a bit of dive into the basics of Enumerator. Hope it proves useful to you! Let me know in the comment section if you do so.