Reputation: 777
The documentation says:
Iterates the given block for each element with an arbitrary object, obj, and returns obj
Iterates the given block for each element with an arbitrary object, obj, and returns obj
But when I tried the below on the both constructs, one gave me the output as expected but the others didn't. So I suspect there is a difference between those two constructs.
each_with_object
%w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase }
=> {"foo"=>"FOO", "bar"=>"BAR"}
success here!
with_object
%w(foo bar).with_object({}) { |str, hsh| hsh[str] = str.upcase }
=> NoMethodError: undefined method `with_object' for ["foo", "bar"]:Array
from (irb):1
from C:/Ruby193/bin/irb:12:in `<main>'
failed here!
So what is the difference between these two methods?
Upvotes: 5
Views: 2567
Reputation: 3379
with object
only works on enumerators, which means that you have to chain it on to something that returns one. eg.
%w(foo bar).each.with_object({}) { |str, h| h[str] = str.upcase }
%w(foo bar).detect.with_object(obj) { ... }
So, you can call with_object
on anything that returns an enumerator if you don't give it a block (such as map
, reduce
, detect
, find_all
...). This includes anything that mixes in Enumerable.
each_with_object
is essentially an alias for each.with_object
.
Upvotes: 6
Reputation: 472
each
returns an Enumerator object.
%w(foo bar).each.class
=> Enumerator
So, for the first case, the array'll be converted to Enumerator
first, then works on the with_object
.
If you want the second case works, you have to convert the array to Enumerator. You can use .to_enum
, .each
, or .map
to convert the array.
%w(foo bar).map.with_object({}) { |str, hsh| hsh[str] = str.upcase }
=> {"foo"=>"FOO", "bar"=>"BAR"}
More details: Enumerator
Upvotes: 7