What is the advantage of creating an enumerable object using to_enum in Ruby? What is the advantage of creating an enumerable object using to_enum in Ruby? ruby ruby

What is the advantage of creating an enumerable object using to_enum in Ruby?


Most built-in methods that accept a block will return an enumerator in case no block is provided (like String#each_char in your example). For these, there is no reason to use to_enum; both will have the same effect.

A few methods do not return an Enumerator, though. In those case you might need to use to_enum.

# How many elements are equal to their position in the array?[4, 1, 2, 0].to_enum(:count).each_with_index{|elem, index| elem == index} #=> 2

As another example, Array#product, #uniq and #uniq! didn't use to accept a block. In 1.9.2, this was changed, but to maintain compatibility, the forms without a block can't return an Enumerator. One can still "manually" use to_enum to get an enumerator:

require 'backports/1.9.2/array/product' # or use Ruby 1.9.2+# to avoid generating a huge intermediary array:e = many_moves.to_enum(:product, many_responses)e.any? do |move, response|  # some criteriaend 

The main use of to_enum is when you are implementing your own iterative method. You typically will have as a first line:

def my_each  return to_enum :my_each unless block_given?  # ...end


I think it has something to do with internal and external Iterators. When you return an enumerator like this:

p = "hello".enum_for(:each_char)

p is an external enumerator. One advantage of external iterators is that:

External iterators are more flexible than internal iterators. It's easy to compare two collections for equality with an external iterator, for example, but it's practically impossible with internal iterators…. But on the other hand, internal iterators are easier to use, because they define the iteration logic for you. [From The Ruby Programming Language book, ch. 5.3]

So, with external iterator you can do, e.g.:

p = "hello".enum_for(:each_char)loop do    puts p.nextend


Let's say we want to take an array of keys and an array of values and sew them up in a Hash:

With #to_enum

def hashify(k, v)  keys = k.to_enum(:each)  values = v.to_enum(:each)  hash = []  loop do    hash[keys.next] = values.next    # No need to check for bounds,    # as #next will raise a StopIteration which breaks from the loop  end  hashend

Without #to_enum:

def hashify(k, v)  hash = []  keys.each_with_index do |key, index|    break if index == values.length    hash[key] = values[index]  end  hashend

It's much easier to read the first method, don't you think? Not a ton easier, but imagine if we were somehow manipulating items from 3 arrays? 5? 10?