tags:

views:

372

answers:

6

What is the slickest, most Ruby-like way of calculating the cumulative sum of an array?

Example:

[1,2,3,4].cumulative_sum

should return

[1,3,6,10]
+1  A: 

The interesting bit is along the lines of

x=0
cumulative_sum = input_array.map do |e| 
                          x = x+e 
                          x
                 end
Steve Gilham
+5  A: 

Here is one way

a = [1, 2, 3, 4]
a.inject([]) { |x, y| x + [(x.last || 0) + y] }

If it is OK that the answer is more than one statement, then this would be cleaner:

outp = a.inject([0]) { |x, y| x + [x.last + y] }
outp.shift # To remove the first 0
hrnt
+11  A: 
class Array
  def cumulative_sum
    sum = 0
    self.map{|x| sum += x}
  end
end
khelll
or just `sum += x`, I guess.
Peter
yes, updated it :P
khelll
+2  A: 
 irb> a = (1..10).to_a
 #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 irb> a.inject([0]) { |(p,*ps),v| [v+p,p,*ps] }.reverse[1..-1]
 #=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

We could also take a note from Haskell and make a ruby version of scanr.

irb> class Array
   >   def scanr(init)
   >     self.inject([init]) { |ps,v| ps.unshift(yield(ps.first,v)) }.reverse
   >   end
   > end
#=> nil
irb> a.scanr(0) { |p,v| p + v }
=> [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
irb> a.scanr(0) { |p,v| p + v }[1..-1]
=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
irb> a.scanr(1) { |p,v| p * v }
=> [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
rampion
+1 for the reference to scanr, that's what an accumulated inject is. Note that you can add scanr to module Enumerable instead of just class Array.
tokland
A: 

One more approach ( though I prefer khell's)

(1..10).inject([]) { |cs, i| cs << i + (cs.last || 0) }

I saw the answer posted by hrnt after posting my answer. Though two approaches look the same, solution above is more efficient as the same array is used in each inject cycle.

a,r = [1, 2, 3, 4],[]
k = a.inject(r) { |x, y| x + [(x.last || 0) + y] }
p r.object_id 
#  35742260
p k.object_id
#  35730450

You will notice r and k are different. If you do the same test for the solution above:

a,r = [1, 2, 3, 4],[]
k = a.inject(r) { |cs, i| cs << i + (cs.last || 0) }
p r.object_id
# 35717730
p k.object_id
# 35717730

The object id for r and k are the same.

KandadaBoggu
A: 

Also you can read about scanl — feature you need, but that isn't yet implement in Ruby, as far as i know. Here are examples and sample source code of it. http://billsix.blogspot.com/2008/11/functional-collection-patterns-in-ruby.html

Nakilon