开发者

How Do I Add Value To All Previous Values In Array

Lets say I have the following array:

my_array = [1, 5, 8, 11, -6]

I need to iterate over this array and add the values prior to the current value together. An example will probably be easier to understand. I need to return an array that should look something like this:

final_array = [1, 6, 14, 25, 19]

I have tried doing something like this:

my_array.collect {|value| value + previous_values }

But obviously that doesn't work because I can't figure out how to get the previous values in the array.

I am a programming noob so this might be e开发者_运维技巧asier than I am making it. I am pretty sure I need to use either collect or inject, but I can't seem to figure out how to do this.

Any help would be appreciated.


My very first instinct was: "That's obviously a scan (aka prefix-sum), so that should be easy":

[1, 5, 8, 11, -6].scan(:+)

Obviously, I've been reading way too much Haskell and Scala lately, because there is no Enumerable#scan in Ruby … yet:

module Enumerable
  def scan(initial=first, &block)
    [initial].tap {|res| 
      reduce {|acc, el| 
        block.(acc, el).tap {|el|
          res << el
        }
      }
    }
  end
end

If you want Enumerable#scan to behave like Enumerable#reduce, i.e. take an optional initial argument and an optional symbol, we need to enhance our version slightly with some argument massaging code stolen from Rubinius's Enumerable#reduce:

module Enumerable
  def scan(initial=nil, sym=nil, &block)
    args = if initial then [initial] else [] end
    unless block_given?
      args, sym, initial = [], initial, first unless sym
      block = ->(acc, el) { acc.send(sym, el) }
    end
    [initial || first].tap {|res| 
      reduce(*args) {|acc, el| 
        block.(acc, el).tap {|e|
          res << e
        }
      }
    }
  end
end

With this enhanced version, the example above now works:

p [1, 5, 8, 11, -6].scan(:+)
# => [1, 6, 14, 25, 19]

If you have this kind of problem again, in another language, remember the terms scan and prefix-sum, such functions are usually pretty common. I don't quite understand why Ruby doesn't have them already.


Your own attempt at it with collect was already very close; just keep summing the previous values as you go.

my_array = [1, 5, 8, 11, -6]
previous_values = 0
my_array.collect { |value| previous_values += value }
# => [1, 6, 14, 25, 19]


x = 0
[1, 5, 8, 11, -6].map {|a| x = x +a }


You can use this:

my_array = [1, 5, 8, 11, -6]
final_array = []

my_array.inject(0) {|res, it| final_array << res + it; res + it}


my_array.each_index{|i| my_array[i] += my_array[i-1] if i>0 }

or

my_array.inject([]){|memo, item| memo << item + memo.last.to_i }


I made a gem for this that pre-allocates the result array. The operation is very fast, even for Enumerables with a large number of elements. Unlike solutions using Enumerable#map, the syntax is exactly like that of Enumerable#reduce, and can optionally use Enumerable#reduce under the hood in case you have monkey-patched #reduce. The name was taken from Clojure's function of the same name.

https://rubygems.org/gems/reductions

To install:

$ gem install reductions

To use:

require 'reductions'

[1, 5, 8, 11, -6].reductions(:+)            #=> [1, 6, 14, 25, 19]
[1, 5, 8, 11, -6].reductions{|a| a + b}     #=> [1, 6, 14, 25, 19]

# With an inital value:
[1, 5, 8, 11, -6].reductions(1,:+)          #=> [1, 2, 7, 15, 26, 20]
[1, 5, 8, 11, -6].reductions(1){|a| a + b}  #=> [1, 2, 7, 15, 26, 20]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜