2

I read this article about enumerable-enumerator. map is method of the Enumerable:

module Enumerable
    def map(*several_variants) ## `*several_variants`  [https://stackoverflow.com/questions/28527931/definition-of-ruby-inbuilt-methods][2]
        #This is a stub, used for indexing
    end
end

class Enumerator  # Enumerator class has included Enumerable module Ok fine !!!
    include Enumerable # many  thing are there ...    
end

And in the Array class:

 class Array # Array class has included Enumerable module it means all the instances methods of  Enumerable module will expose as the instance methods in  Array class !!! Okk
     include Enumerable  # many  thing are there ...
 end

Now when I call the map method on an array, I get an Enumerator:

[1,2,3,4].map   => #<Enumerator: [1, 2, 3, 4]:map> 

1: What is Enumerator in the #<Enumerator: [1, 2, 3, 4]:map> line of output above?

 module Test

  def initialize(str)
    @str = str
  end

  def methods_1(str)
    p "Hello!!#{str}"
  end
 end

 class MyTest
  include Test
  include Enumerable
 end

 my_test = MyTest.new('Ruby')
 p  "Which class ? : #{my_test}"
 my_test.methods_1('Rails')
 p my_test.map

Output

"Which class ? : #<MyTest:0x000000019e6e38>"
"Hello!!Rails"
#<Enumerator: #<MyTest:0x000000019e6e38 @str="Ruby">:map> 

2: This should be object of MyTest class only if i'm not wrong.

3: In this line #<Enumerator: #<MyTest:0x000000019e6e38 @str="Ruby">:map>, what does Enumerator do here?

Thanks for all to making this concept clear, in short span of time. I would like to share some useful link to grasp the concept at great level.

Map, Select, and Other Enumerable Methods

What does the “map” method do in Ruby?

Enumerable and Enumerator

Ruby tricks to make your code more fun and less readable

Community
  • 1
  • 1
  • 1
    Your second "question" is not a question. What does "This should be object of MyTest class only if i'm not wrong" mean? – Amit Kumar Gupta Feb 16 '15 at 07:47
  • what I’m trying to say is , I’m calling `map` method of `Enumerable module` on `my_test` object which is the object of `MyTest` `class` so it should be return `MyTest` class only –  Feb 16 '15 at 08:11
  • That's not true at all. Why would `map` return an instance of `MyTest`? What does this have to do with the `Enumerable` module? `methods_1` is from the `Test` module, do you expect `my_test.methods_1('Rails')` to return an instance of `MyTest` as well? – Amit Kumar Gupta Feb 16 '15 at 08:54

3 Answers3

1

I don't think your question is specific to Enumerable#map. If you browse the Enumerable module, you'll find many methods (including map) that return one thing if a block is given and an enumerator if no block is given. Suppose you wrote,

[1,2,3].map.with_index { |x,idx| x+idx } #=> [1,3,5]

map is not given a block, so it returns an enumerator:

enum1 = [1,2,3].map
  #=> #<Enumerator: [1, 2, 3]:map>

The method Enumerator#with_index is then sent to enum1:

enum2 = enum1.with_index
  #=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:with_index>

As you see, enum2 is also an enumerator, a sort of "compound enumerator".

enum2 does have a block, so it's elements are passed into the block by the method Enumerator#each, which calls Array#each.

We can view the contents of any enumerator by converting it to an array:

enum2.to_a
  #=> [[1, 0], [2, 1], [3, 2]]

This shows us the elements that each will pass into the block, the first being the array [1, 0], causing the block variable x to be assigned the value 1 and the block variable idx to be assigned 0.

The final step in the calculation is performed by Array#each:

enum2.each { |x,idx| x+idx } #=> [1,3,5]

The docs for Enumerator#with_index (as well as for map and most other Enumerable methods) says that it is returns an object (generally not an enumerator) when it is given a block, and returns an enumerator when it is not given a block. As I've just shown, however, it's really producing an enumerator in any case; when it is followed by a block, it call's upon the receiver's class' each method to do its thing.

Yes, you can include Enumerable in your owns classes, but to use that module's methods you must also define the method each for your class.

It's enumerators that allow us to chain methods, and to do that efficiently, without creating temporary arrays and other such objects between the links.

I once gave the following fanciful account of how the Enumerable module came into existence:

One day, long ago, a very wise Rubyest from the land of the Rising Sun noticed that many methods he used for arrays were very similar to those he used for hashes, ranges and other collections. He saw that he could write them so that the only difference was how the method "each" was implemented, so he put them all in a module he called "可算の" ("Enumerable"), and then in all the classes for different types of collections he added "include Enumerable" and a method "each". After doing this, he thought, "生活は快適です" ("life is good").

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • now i get it . one more thing why we need to create each method? as you wrote in your answer _so that the only difference was how the method "each" was implemented_ –  Feb 16 '15 at 09:53
  • My apologies for the late reply. Enumerable methods must know how to pass objects from the receiver into the block, which varies by class. If `each` were not defined by class you could not change it, and you could not `include Enumerable` in your own classes. Here's an analogy: [Yakima](http://www.yakima.com/) makes roof racks for hundreds of different makes and models of cars. The way they do it is to sell "universal racks" that attach to your car with a small piece of hardware designed specifically for your car. That's their `each` method. Does that answer your question? – Cary Swoveland Feb 18 '15 at 06:47
0

The basic idea of map method is that, for every iteration of an element, map inserts the value of the last expression executed in the block into a new array. After the last element is executed, map returns to you the new array of values.

In your example:

  1. Enumerator is just a class in Ruby. You've instantiated it when you didn't add a block on [1,2,3,4].map. Take note that it's different from Enumerable

  2. Yes, it is your MyTest class. If you like to test it, do my_test.class

  3. You just instantiated an Enumerator object by doing my_test.map and ruby saw that you've included the Enumerable module in your class.

Finks
  • 1,661
  • 2
  • 16
  • 25
  • Ok @Finks i checked with the Enumerator and Enumerable again but still one doubt `[1,2,3,4].map => # ` why `Enumerator` came here .. Can you give me any Example.. –  Feb 16 '15 at 08:49
  • Because there are multiple ways to instantiate an `Enumerator`, 1 is adding `map` method like what you did. You can also do `[1,2,3,4].each` or just using any of the `Enumerable` methods. – Finks Feb 16 '15 at 10:26
0

The most common usage of map is with a block, like so:

[1, 2, 3, 4].map { |n| n*n }
# => [1, 4, 9, 16]

However, if you call map without passing a block, it still returns something. In this case it returns an Enumerator which knows about the original array and that it's a "map" type of array. It can be used to chain other Enumerator methods, for instance:

[1, 2, 3, 4].map.with_index { |n, idx| "entry #{idx} squared: #{n*n}" }
# => ["entry 0 squared: 1", "entry 1 squared: 4", "entry 2 squared: 9", "entry 3 squared: 16"]

You can read more documentation on the Enumerator class but in practice, you probably want to understand how to use map with blocks more.

Amit Kumar Gupta
  • 17,184
  • 7
  • 46
  • 64
  • could you please explain a bit about the line that you written _In this case it returns an Enumerator which knows about the original array and that it's a "map" type of array._ –  Feb 16 '15 at 09:20