I'm trying to figure out how each_with_object
is supposed to be used.
I have a sum example that doesn't work:
> (1..3).each_with_object(0) {|i,sum| sum+=i}
=> 0
I would assume that the result would be 6 ! Where is my mistake ?
The most useful and I think the most popular usage of each_with_object is putting hash or array as an argument. You can do this like in example below: I know this examples are trivial, but they show main rules. You don’t need to declare array or hash before your loops for example like this: I know this example can be also replace with map use case.
You can create an object using an object initializer. Alternatively, you can first create a constructor function and then instantiate an object using that function and the new operator. In addition to creating objects using a constructor function, you can create objects using an object initializer.
The concept of objects in JavaScript can be understood with real life, tangible objects. In JavaScript, an object is a standalone entity, with properties and type. Compare it with a cup, for example. A cup is an object, with properties. A cup has a color, a design, weight, a material it is made of, etc.
each_with_object
does not work on immutable objects like integer.
(1..3).each_with_object(0) {|i,sum| sum += i} #=> 0
This is because each_with_object
iterates over a collection, passing each element and the given object to the block. It does not update the value of object after each iteration and returns the original given object.
It would work with a hash since changing value of a hash key changes it for original object by itself.
(1..3).each_with_object({:sum => 0}) {|i,hsh| hsh[:sum] += i}
#=> {:sum => 6}
String
objects are interesting case. They are mutable so you might expect the following
to return "abc"
("a".."c").each_with_object("") {|i,str| str += i} # => ""
but it does not. This is because str += "a"
returns a new object and the original object stays the same. However if we do
("a".."c").each_with_object("") {|i,str| str << i} # => "abc"
it works because str << "a"
modifies the original object.
For more info see ruby docs for each_with_object
For your purpose, use inject
(1..3).inject(0) {|sum,i| sum += i} #=> 6
# or
(1..3).inject(:+) #=> 6
A simple, but common example of using each_with_object
is when you need to build a hash depending on elements in an array. Very often you see something like:
hash = {}
[1, 2, 3, 4].each { |number| hash[number] = number**2 }
hash
Using each_with_object
avoids the explicit initialization and return of the hash
variable.
[1,2,3,4].each_with_object({}) { |number, hash| hash[number] = number**2 }
I advise reading the docs for inject
, tap
, and each_with_index
. These methods are helpful when you aim for short and readable code.
The documentation of Enumerable#each_with_object
is very clear :
Iterates the given block for each element with an arbitrary object given, and returns the initially given object.
In your case, (1..3).each_with_object(0) {|i,sum| sum+=i}
,you are passing 0
,which is immutable object. Thus here the intial object to each_with_object
method is 0
,so method returns 0
.It works at it is advertised. See below how each_with_object
works ,
(1..3).each_with_object(0) do |e,mem|
p "#{mem} and #{e} before change"
mem = mem + e
p mem
end
# >> "0 and 1 before change"
# >> 1
# >> "0 and 2 before change"
# >> 2
# >> "0 and 3 before change"
# >> 3
That means in every pass,mem
is set to initial passed object. You might be thinking of the in first pass mem
is 0
,then in next pass mem
is the result of mem+=e
,i.e. mem
will be 1
.But NO,in every pass mem
is your initial object,which is 0
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With