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]
精彩评论