user3195547
user3195547

Reputation:

How to understand the core concept of #map method

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

Upvotes: 2

Views: 241

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110675

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").

Upvotes: 1

Amit Kumar Gupta
Amit Kumar Gupta

Reputation: 18567

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.

Upvotes: 0

Finks
Finks

Reputation: 1681

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.

Upvotes: 0

Related Questions