Enumerable.inject_with_index?

Enumerable.inject is pretty cool, but it treat the elements as if they
have
the same weight when doing the calculation, in my case I need to apply a
different weight based on the index of each element, therefore I also
need
the index being provided as one more block parameter.

enum.find_index might help, but I think there should be a more direct
way to
get the index.

In short, like for enum.each_entry, there’s each_with_index,
similarly, for enum.inject, is it possible to have
enum.inject_with_index {
|memo, obj, index| … } ?

Thanks

On Sun, Jan 23, 2011 at 10:31 PM, redstun [email protected] wrote:

Enumerable.inject is pretty cool, but it treat the elements as if they have
the same weight when doing the calculation, in my case I need to apply a
different weight based on the index of each element, therefore I also need
the index being provided as one more block parameter.
enum.find_index might help, but I think there should be a more direct way to
get the index.
In short, like for enum.each_entry, there’s each_with_index,
similarly, for enum.inject, is it possible to have enum.inject_with_index {
|memo, obj, index| … } ?

how about,

(0…5).each.with_index.inject(0){|sum,(i,j)| sum+j}
#=> 15

(0…5).each.with_index.inject(0){|sum,(i,j)| sum+i*j}
#=> 55

best regards -botp

On Sun, Jan 23, 2011 at 11:00 PM, botp [email protected] wrote:

(0…5).each.with_index.inject(0){|sum,(i,j)| sum+j}
#=> 15
(0…5).each.with_index.inject(0){|sum,(i,j)| sum+i*j}
#=> 55

sorry if that wasn’t so clear. again,

(3…5).each.with_index.inject(0){|sum,(value,index)| sum+value}
#=> 12

(3…5).each.with_index.inject(0){|sum,(value,index)| sum+index}
#=> 3

(3…5).each.with_index.inject(0){|sum,(value,index)| sum+index*value}
#=> 14

kind regards -botp

I’m curious as to when and why people use each_with_index and/or
inject, etc, instead of a somewhat less elegant, and admittedly
possibly less flexible, more direct approach. (Which usually(?) takes
about the same number of characters for the code.)

ary = [3, 4, 5]
rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v * index
}
p rrr #=> 14
index = -1; rrr = ary.inject(0) { |sum, v| index += 1; sum + v * index }
p rrr #=> 14
rrr = 0; ary.each_with_index { |v, index| rrr += v * index }; rrr
p rrr #=> 14
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }; rrr
p rrr #=> 14

require “benchmark”
kt = 500_000
Benchmark.bmbm() do |bm|
bm.report( “each_with_index.inject(0)” ) { kt.times {
rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v *
index }
} }
bm.report( “inject(0)” ) { kt.times {
index = -1; rrr = ary.inject(0) { |sum, v| index += 1; sum + v *
index }
} }
bm.report( “each_with_index” ) { kt.times {
rrr = 0; ary.each_with_index { |v, index| rrr += v * index }
} }
bm.report( “each” ) { kt.times {
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }
} }
end

#=>
Rehearsal -------------------------------------------------------------
each_with_index.inject(0) 2.410000 0.000000 2.410000 ( 2.404242)
inject(0) 1.000000 0.000000 1.000000 ( 1.000677)
each_with_index 0.840000 0.010000 0.850000 ( 0.846287)
each 0.640000 0.000000 0.640000 ( 0.640142)
---------------------------------------------------- total: 4.900000sec

                            user     system      total        real

each_with_index.inject(0) 2.380000 0.020000 2.400000 ( 2.392590)
inject(0) 1.000000 0.000000 1.000000 ( 0.999785)
each_with_index 0.850000 0.000000 0.850000 ( 0.846178)
each 0.640000 0.000000 0.640000 ( 0.639366)

(My apologies if the benchmarks are ragged and unaligned.)

Cool! thanks!

I was working with array, so I can write
[3, 4, 5].each_with_index.inject(0) { |sum, (value, index)| sum + value
*
index } #=> 14

OR
ary = [3, 4, 5]
ary.each_index.inject(0) { |sum, index| sum + ary[index] * index } #=>
14

works like a charm :slight_smile:

botp, Thank you very much!

BTW, one more finding is that,
puts([3, 4, 5].each_with_index.inject(0) do |sum, (value, index)| sum +
value * index end)
puts([3, 4, 5].each_with_index.inject(0) { |sum, (value, index)| sum +
value

  • index })
    puts [3, 4, 5].each_with_index.inject(0) { |sum, (value, index)| sum +
    value
  • index }
    puts [3, 4, 5].each_with_index.inject(0) do |sum, (value, index)| sum +
    value * index end

all works but the last one, it reports TypeError: 0 is not a symbol,
actually caused by the block being passed to puts instead of inject,
then
without a block, inject expect a symbol as argument. hum, just a note to
myself.

Interesting, thanks Colin.

As you said, the code you demonstrated, could performer better,
meanwhile
it’s somewhat less elegant.

IMHO the code is ok, we just have more space inside the
interpreter/runtime to get improved, like in JVM, the JIT compiler could
compile all of the 4 lines of code in to the same machine instructions
and
yield the same performance.

The dynamic nature and elegance design of Ruby certainly sacrificed
performance at somewhat level, but that doesn’t stop you and me from
using
it, right? :slight_smile:

Anyway, thanks for the benchmark demo, I just ran it on my machine, with
both MRI and JRuby,
MRI 1.8.7
user system total real
each_with_index.inject(0) 4.930000 0.000000 4.930000 ( 5.036288)
inject(0) 3.088000 0.000000 3.088000 ( 3.080176)
each_with_index 2.668000 0.000000 2.668000 ( 2.668153)
each 1.700000 0.000000 1.700000 ( 1.713098)

JRuby 1.5.6
user system total real
each_with_index.inject(0) 1.918000 0.000000 1.918000 ( 1.918000)
inject(0) 1.058000 0.000000 1.058000 ( 1.058000)
each_with_index 0.979000 0.000000 0.979000 ( 0.979000)
each 0.624000 0.000000 0.624000 ( 0.624000)

Like your result the raw each runs the fastest, but we can also find
that,
the result of each_with_index.inject with JRuby is already close to that
of
raw each with MRI.

Colin B. wrote in post #977259:

I’m curious as to when and why people use each_with_index and/or
inject, etc, instead of a somewhat less elegant, and admittedly
possibly less flexible, more direct approach.

I think you answered it yourself: people will generally choose the more
elegant and/or clearer solution (by their own assessment of ‘clearer’).

Unless this is the innermost loop of a program which processes a large
dataset, and you’ve used a profiler to prove that this is the hotspot in
your particular application, the performance difference doesn’t matter a
hoot.

On 27 January 2011 01:42, redstun [email protected] wrote:

The dynamic nature and elegance design of Ruby certainly sacrificed
each 1.700000 0.000000 1.700000 ( 1.713098)
raw each with MRI.

rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v * index }
Benchmark.bmbm() do |bm|
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }

           user   system   total    real

each_with_index.inject(0) 2.380000 0.020000 2.400000 ( 2.392590)
inject(0) 1.000000 0.000000 1.000000 ( 0.999785)
each_with_index 0.850000 0.000000 0.850000 ( 0.846178)
each 0.640000 0.000000 0.640000 ( 0.639366)

(My apologies if the benchmarks are ragged and unaligned.)

Just to complete with 1.9 (ruby 1.9.3dev (2011-01-26 trunk 30659)
[x86_64-darwin10.6.0]) results:

Rehearsal -------------------------------------------------------------
each_with_index.inject(0) 1.220000 0.000000 1.220000 ( 1.220411)
inject(0) 0.560000 0.000000 0.560000 ( 0.568007)
each_with_index 0.510000 0.010000 0.520000 ( 0.516797)
each 0.400000 0.000000 0.400000 ( 0.405471)
---------------------------------------------------- total: 2.700000sec

                            user     system      total        real

each_with_index.inject(0) 1.250000 0.000000 1.250000 ( 1.268727)
inject(0) 0.570000 0.010000 0.580000 ( 0.582921)
each_with_index 0.510000 0.000000 0.510000 ( 0.510591)
each 0.390000 0.000000 0.390000 ( 0.401833)

Only each_with_index.inject is notably slower, others are similar.

guess we find a spot to improve when chaining up multiple enum methods?

btw, looks like you guys both have more powerful computer than mine. :wink:

Colin B. wrote in post #977259:

I’m curious as to when and why people use each_with_index and/or
inject, etc, instead of a somewhat less elegant, and admittedly
possibly less flexible, more direct approach.

I think that might be the very definition of preemptive optimization.
Make it work first, then make it fast… if you need to.