Reputation:
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?
Ruby tricks to make your code more fun and less readable
Upvotes: 2
Views: 241
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
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
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:
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
Yes, it is your MyTest class. If you like to test it, do my_test.class
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