Creating an array of cumulative sum in javascript
JavascriptArraysJavascript Problem Overview
This is an example of what I need to do:
var myarray = [5, 10, 3, 2];
var result1 = myarray[0];
var result2 = myarray[1] + myarray[0];
var result3 = myarray[2] + myarray[1] + myarray[0];
var result4 = myarray[3] + myarray[2] + myarray[1] + myarray[0];
so all that would output 5, 15, 18, 20
but instead of writing out all the vars like that, I want it to say something like:
var result = arrayitem + the sum of any previous items
Does that make sense? Is that possible? How do I do that?
Javascript Solutions
Solution 1 - Javascript
An elegant solution copied from Nina Scholz, using currying to access the previous value.
const cumulativeSum = (sum => value => sum += value)(0);
console.log([5, 10, 3, 2].map(cumulativeSum));
cumulativeSum
is the function value => sum += value
, with sum
initialized to zero. Every time it's called, sum
is updated and will equal the previous value (output[n-1]) when called the next time (with input[n]).
Note that sum
will need to be set to zero explicitly when you want to reuse the summation. The most convenient way to handle this may be to just inline the code instead of using a named function.
console.log([5, 10, 3, 2].map((sum => value => sum += value)(0)));
console.log([6, 10, 3, 2].map((sum => value => sum += value)(0)));
vs the unwanted behavior
const cumulativeSum = (sum => value => sum += value)(0);
console.log([5, 10, 3, 2].map(cumulativeSum));
console.log([6, 10, 3, 2].map(cumulativeSum));
Solution 2 - Javascript
Javascript's reduce
provides the current index, which is useful here:
var myarray = [5, 10, 3, 2];
var new_array = [];
myarray.reduce(function(a,b,i) { return new_array[i] = a+b; },0);
new_array // [5, 15, 18, 20]
Solution 3 - Javascript
Alternative reduce
approach that avoids making new arrays:
var result = myarray.reduce(function(r, a) {
r.push((r.length && r[r.length - 1] || 0) + a);
return r;
}, []);
There's no need to re-sum the subarrays for each result.
edit less ugly version of the same thing:
var result = myarray.reduce(function(r, a) {
if (r.length > 0)
a += r[r.length - 1];
r.push(a);
return r;
}, []);
Solution 4 - Javascript
A couple more options with ES6 array spreading
[1, 2, 3].reduce((a, x, i) => [...a, x + (a[i-1] || 0)], []); //[1, 3, 6]
or
[3, 2, 1].reduce((a, x, i) => [...a, a.length > 0 ? x + a[i-1] : x], []); //[3, 5, 6]
Solution 5 - Javascript
Simple solution using ES6
let myarray = [5, 10, 3, 2];
let new_array = [];
myarray.reduce( (prev, curr,i) => new_array[i] = prev + curr , 0 )
console.log(new_array);
For more information Array.reduce()
Solution 6 - Javascript
I needed to keep the results and just add a running total property. I had a json object with a date and revenue and wanted to display a running total as well.
//i'm calculating a running total of revenue, here's some sample data
let a = [
{"date": "\/Date(1604552400000)\/","revenue": 100000 },
{"date": "\/Date(1604203200000)\/","revenue": 200000 },
{"date": "\/Date(1604466000000)\/","revenue": 125000 },
{"date": "\/Date(1604293200000)\/","revenue": 400000 },
{"date": "\/Date(1604379600000)\/","revenue": 150000 }
];
//outside accumulator to hold the running total
let c = 0;
//new obj to hold results with running total
let b = a
.map( x => ({...x,"rtotal":c+=x.revenue}) )
//show results, use console.table if in a browser console
console.log(b)
Solution 7 - Javascript
How about this solution
var new_array = myarray.concat(); //Copy initial array
for (var i = 1; i < myarray.length; i++) {
new_array[i] = new_array[i-1] + myarray[i];
}
console.log(new_array);
PS: You can use the original array as well. I just copied it in case we don't want to pollute it.
Solution 8 - Javascript
This question has been answered well by others but I'll leave my solution here too. I tried to be concise without sacrificing clarity.
myarray.reduce((a, e, i) => {
// a: Accumulator; e: current Element; i: current Index
return a.length > 0 ? [...a, e + a[i - 1]] : [e];
}, []);
Map, Filter, Reduce, Find, Some, etc. are highly underrated.
Solution 9 - Javascript
I came up with this ES6 one using array.map()
function prefixSum(nums) {
let psum = 0;
return nums.map(x => psum += x);
};
console.log(prefixSum([5, 10, 20, 30]));
Solution 10 - Javascript
My initial ES6 thought was similar to a few above answers by Taeho and others.
const cumulativeSum = ([head, ...tail]) =>
tail.reduce((acc, x, index) => {
acc.push(acc[index] + x);
return acc
}, [head])
console.log(cumulativeSum([-1,2,3])
The solution performs:
n lookups, n - 1 sums and 0 conditional evaluations
Most of what I saw above appeared to use:
n lookups, 2n sums, and n conditional evaluations:
You could do this with ie6 safe js as well. This is possibly more efficient since you don't have to create the tail spread array.
function cumulativeSum(a) {
var result = [a[0]];
var last = a[0];
for (i = 1; i < a.length; i++) {
last = last + a[i];
result.push(last)
}
return result;
}
console.log(cumulativeSum([-1,2,3]))
Solution 11 - Javascript
A more generic (and efficient) solution:
Array.prototype.accumulate = function(fn) {
var r = [this[0]];
for (var i = 1; i < this.length; i++)
r.push(fn(r[i - 1], this[i]));
return r;
}
or
Array.prototype.accumulate = function(fn) {
var r = [this[0]];
this.reduce(function(a, b) {
return r[r.length] = fn(a, b);
});
return r;
}
and then
r = [5, 10, 3, 2].accumulate(function(x, y) { return x + y })
Solution 12 - Javascript
use reduce to build the result directly and non-destructively.
a.reduce(function(r,c,i){ r.push((r[i-1] || 0) + c); return r }, [] );
Solution 13 - Javascript
Simple solution using for loop
var myarray = [5, 10, 3, 2];
var output = [];
var sum = 0;
for(var i in myarray){
sum=sum+myarray[i];
output.push(sum)
}
console.log(output)
Solution 14 - Javascript
Another clean one line solution with reduce and concat
var result = myarray.reduce(function(a,b,i){ return i === 0 ? [b]: a.concat(a[i-1]+b);},0);
//[5, 10, 3, 2] => [5, 15, 18, 20]
Solution 15 - Javascript
A simple function using array-reduce.
const arr = [6, 3, -2, 4, -1, 0, -5];
const prefixSum = (arr) => {
let result = [arr[0]]; // The first digit in the array don't change
arr.reduce((accumulator, current) => {
result.push(accumulator + current);
return accumulator + current; // update accumulator
});
return result;
}
Solution 16 - Javascript
Returns sorted obj by key and sorted array!!!
var unsorted_obj = {
"2016-07-01": 25,
"2016-07-04": 55,
"2016-07-05": 84,
"2016-07-06": 122,
"2016-07-03": 54,
"2016-07-02": 43
};
var sort_obj = function(obj){
var keys = [];
var sorted_arr = [];
var sorted_obj = {};
for(var key in obj){
if(obj.hasOwnProperty(key)){
keys.push(key);
}
}
keys.sort();
jQuery.each(keys, function(i, key){
sorted_obj[key] = obj[key];
var val = obj[key];
sorted_arr.push({
idx: i,
date: key,
val: val
})
});
return { sorted_obj: sorted_obj, sorted_arr: sorted_arr };
};
var sorted_obj = sort_obj(unsorted_obj).sorted_obj;
var sorted_arr = sort_obj(unsorted_obj).sorted_arr;
// sorted_arr = [{"idx":0,"date":"2016-07-01","val":25},{"idx":1,"date":"2016-07-02","val":43},{"idx":2,"date":"2016-07-03","val":54},...]
// sorted_obj = {"2016-07-01":25,"2016-07-02":43,"2016-07-03":54,...}
Solution 17 - Javascript
To keep the cumsum within a function until fully built, I offer this minor variant on Matt's Answer:
var cumsum = function(past_sums, new_value) {
var last_sum = 1*past_sums.slice(-1);
var new_sum = last_sum + new_value;
return past_sums.concat([new_sum]);
}
var some_sums = [5, 10, 3, 2].reduce(cumsum, []);
Here's how it works:
- The first cycle:
past_sums.slice(-1) === []
1*past_sums.slice(-1) === 0
- All but the last cycle:
cumsum
returns [past_sums
andnew_sum
] as next cycle'spast_sums
- The last cycle:
cumsum
returns[5, 15, 18, 20]
as the output Arraysome_sums
It can be written with fewer lines:
var cumsum = function(sums, val) {
return sums.concat([ val + 1*sums.slice(-1) ]);
}
var some_sums = [5, 10, 3, 2].reduce(cumsum, []);
With Arrow Functions (Not for ≤IE11 or Opera Mini), I'd write this:
var cumsum = (sums,val) => sums.concat([ val + 1*sums.slice(-1) ]);
var some_sums = [5, 10, 3, 2].reduce(cumsum, []);
Solution 18 - Javascript
Use arrow function instead of function, comma operator instead of return, and currentIndex in reduce callback.
[5, 10, 3, 2].reduce((r, a, i) => (r.push((i && r[i - 1] || 0) + a), r), []); // [ 5, 15, 18, 20 ]
Solution 19 - Javascript
/**
* Turn an array of numbers to cumulative sum array
* @param { Array } [1,2,3,4,5]
* @return { Array } [1,3,6,10,15]
*/
const accumulate = (a, c) => a + c
const cusum = arr => arr.map((v, i, data) => {
return data.slice(0, i + 1).reduce(accumulate)
})
Solution 20 - Javascript
Old school and simpler :
let myarray = [5, 10, 3, 2], result = [];
for (let i = 0, s = myarray[0]; i < myarray.length; i++, s += myarray[i]) result.push(s);
console.log(result); // [5, 15, 18, 20]
Solution 21 - Javascript
/*Checkout the explanation below */
nums = [1,2,3,4]
var runningSum = function(nums) {
shoppingCart =[];
runningtotal =0;
nums.forEach(EachValue => {
runningtotal += EachValue
shoppingCart.push(runningtotal);
});
return shoppingCart
};
console.log(runningSum(nums));
/* define some numbers*/
nums = [1,2,3,4]
/* assign function runningSum , some numbers*/
var runningSum = function(nums) {
shoppingCart =[]; /* Create a empty shopping cart to store the items */
runningtotal =0; /* Start with your beginning bill of zero items in the cart */
/* remove a number from list of numbers call each one of the numbers from the array => EachValue using a pointer function*/
nums.forEach(EachValue => {
(runningtotal += EachValue)
/* Now add the value to the runningtotal to represent items prices*/
shoppingCart.push(runningtotal);
/* Use the push method to place it into the new array called shopping cart */ });
return shoppingCart
/* output the items currently in the shopping cart with only 1d prices */
};
nums = [1,2,3,4]
var runningSum = function(nums) {
shoppingCart =[];
runningtotal =0;
nums.forEach(EachValue => {
runningtotal += EachValue
shoppingCart.push(runningtotal);
});
return shoppingCart
};
console.log(runningSum(nums));
Solution 22 - Javascript
var nums= [5, 10, 3, 2];
var runningSum = function(nums) {
nums.reduce((acc, _, i) => (nums[i] += acc));
return nums;
};
Solution 23 - Javascript
Here's a simple answer using recursion.
function sumArrayRec(arr) {
return sumArrayHelper(0, 0, [], arr)
}
function sumArrayHelper(prevSum, index, sums, arr) {
if (!arr.length) {
return sums
}
let curSum = arr[index] + prevSum
sums.push(curSum)
array.shift()
return sumArrayHelper(curSum, index++, sums, array)
}