bill
bill

Reputation: 368

Analysis of .inject vs .each_with_object

I know that each_with_object does not work on immutable objects like integer.

For example (1..3).each_with_object(0) {|i,sum| sum += i} #=> 0

We can fix this with inject, and the sum will be 6. Great!

Lets take another example

("a".."c").each_with_object("") {|i,str| str += i} # => ""

This won't work either since when we do str+=i, we make a new object

Instead we need to do

("a".."c").each_with_object("") {|i,str| str << i} # => "abc"

Why is it that when I do inject in the following two ways I get weird results

("a".."c").inject("") {|i,str| str += i} # => cba

("a".."c").inject("") {|i,str| str << i} # => ERROR

Why is the first printing abc in reverse? And why is the second an error?

I know that we should use .each_with_object for mutable objects, and inject for immutable (like Integer), but can I use .inject for mutable objects as well just like .each_with_object? Can anyone explain these results?

Upvotes: 4

Views: 1041

Answers (1)

Schwern
Schwern

Reputation: 164689

Why is the first printing abc in reverse?

The arguments are backwards. See Enumerable#inject.

Printing i and str shows the problem.

$ ruby -e '("a".."c").inject("") {|i,str| puts "i   = #{i}"; puts "str = #{str}"; ret = str += i; puts "ret = #{ret}\n\n"; ret }'
i   = 
str = a
ret = a

i   = a
str = b
ret = ba

i   = ba
str = c
ret = cba

i is the accumulated sum returned the last time through the block. str is the next value. You're adding the previous accumulated value to then end of the next one. You have the arguments backwards. The first one is the sum, the second is the next element.

When that's corrected it works fine.

$ ruby -e 'puts ("a".."c").inject("") {|sum,i| sum += i}'
abc

Furthermore, it's the return value of the block that's used. Altering str has no effect, it just confuses things further.

$ ruby -e 'puts ("a".."c").inject("") {|sum,i| sum + i}'
abc

And why is the second an error?

It isn't, not in Ruby 2.0.0 nor 2.2.4 anyway. But it has the same problem of backwards args.

$ ruby -e 'puts ("a".."c").inject("") {|i,str| str << i} # => ERROR'
cba

$ ruby -v
ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin15]

Upvotes: 3

Related Questions