5

In Ruby I'm trying to understand between the to_enum and enum_for methods. Before I my question, I've provided some sample code and two examples to help w/ context.

Sample code:

# replicates group_by method on Array class
class Array
  def group_by2(&input_block)
    return self.enum_for(:group_by2) unless block_given?
    hash  = Hash.new {|h, k| h[k] = [] }
    self.each { |e| hash[ input_block.call(e) ] << e }
    hash
  end
end

Example # 1:

irb (main)> puts [1,2,3].group_by2.inspect
=> #<Enumerator: [1, 2, 3]:group_by2> 

In example #1: Calling group_by on the array [1,2,3], without passing in a block, returns an enumerator generated with the command self.enum_for(:group_by_2).

Example #2

irb (main)> puts [1,2,3].to_enum.inspect
=> #<Enumerator: [1, 2, 3]:each> 

In example #2, the enumerator is generated by calling the to_enum method on the array [1,2,3]

Question:

Do the enumerators generates in examples 1 and 2, behave differently in any way? I can see from the inspected outputs that they show slightly different labels, but I can find any difference in the enumerators' behavior.

# Output for example #1
#<Enumerator: [1, 2, 3]:each> # label reads ":each"

# Output for example #2
#<Enumerator: [1, 2, 3]:group_by2> # label reads ":group_by2"
popedotninja
  • 1,170
  • 1
  • 12
  • 23

1 Answers1

13
p [1, 2, 3].to_enum
p [1, 2, 3].enum_for

--output:--

#<Enumerator: [1, 2, 3]:each>
#<Enumerator: [1, 2, 3]:each>

From the docs:

to_enum

Creates a new Enumerator which will enumerate by calling method on obj, passing args if any.

...

enum_for

Creates a new Enumerator which will enumerate by calling method on obj, passing args if any.

ruby is a language that often has method names that are synonyms.

Followup question:

Does the symbol in the command [1,2,3].to_enum(:foo) serve a purpose, other than replacing :each with :foo in the output?

Yes. By default, ruby hooks up the enumerator to the receiver's each() method. Some classes do not have an each() method, for instance String:

str = "hello\world"
e = str.to_enum
puts e.next

--output:--
1.rb:3:in `next': undefined method `each' for "helloworld":String (NoMethodError)
    from 1.rb:3:in `<main>

to_enum() allows you to specify the method you would like the enumerator to use:

str = "hello\nworld"
e = str.to_enum(:each_line)
puts e.next

--output:--
hello

Now, suppose you have the array [1, 2, 3], and you want to to create an enumerator for your array. An array has an each() method, but instead of creating an enumerator with each(), which will return each of the elements in the array, then end; you want to create an enumerator that starts over from the beginning of the array once it reaches the end?

e = [1, 2, 3].to_enum(:cycle)

10.times do
  puts e.next()
end

--output:--
1
2
3
1
2
3
1
2
3
1
7stud
  • 46,922
  • 14
  • 101
  • 127
  • @ 7stud thanks. Just wanted to make sure I wasn't missing something. – popedotninja May 24 '14 at 23:53
  • 2
    @amorphid, The ruby docs used to be pretty good about saying, "This method is a synonym for method A. See description for method A." I'd rather see that referral than a duplicate description. – 7stud May 25 '14 at 02:24
  • I just reread your example, and just now realized that one can also do `[1,2,3].to_enum(:foo)`, which returns `#`. Had I realized that, my question would have simply been => Does the symbol in the command `[1,2,3].to_enum(:foo)` serve a purpose, other than replacing `:each` with `:foo` in the output? The [docs for the Enumerator class](http://www.ruby-doc.org/core-2.1.2/Enumerator.html) say nothing about it, at least not that I can see. – popedotninja May 25 '14 at 05:54
  • @amorphid, I edited my post to answer your followup question. – 7stud May 28 '14 at 00:56
  • Thanks. This is exactly the answer I was looking for! It just took me a little while to figure out how to ask the question :) – popedotninja May 29 '14 at 08:14
  • @CarySwoveland. Hi Cary. :) – 7stud Apr 10 '15 at 21:07