Reputation: 1143
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?
Upvotes: 62
Views: 66191
Reputation: 135
Adapting Matt's answer to ES6, this is the most readable to me:
const array = [5, 10, 3, 2];
const cumpts = [];
array.reduce((a, b, i) => cumpts[i] = a + b, 0);
console.log(cumpts);
Upvotes: 0
Reputation: 970
Could you do this in a one-liner? Sure.
const values = [5, 10, 4, 2];
const sums = values.reduce((a, x, i) => [...a, x + (i && a[i-1])], []);
Should you? Probably not. This is more readable:
function cumulativeSums(values) {
let total = 0;
const sums = [];
values.forEach(v => {
total += v;
sums.push(total);
});
return sums;
}
const values = [5, 10, 4, 2];
const sums = cumulativeSums(values);
Upvotes: 1
Reputation: 115
This is what I found to be the most intuitive:
arr.reduce((a, x) => [...a, a[a.length - 1] + x], [0]).slice(1)
To get around zero index issues (in the other answers), just add a zero at the start, and strip it off later.
Upvotes: 1
Reputation: 184
here's a simpler solution, using array.reduce that keeps track of all the sum and push it in array to make a new runningSum Array input: [5, 10, 3, 2] output: [ 5, 15, 18, 20 ]
const arr = [5, 10, 3, 2];
const sumArr = [];
arr.reduce((previousValue, currentValue) => {
const sum = previousValue + currentValue;
sumArr.push(sum);
return sum;
}, 0);
console.log("sumArr ", sumArr);
Upvotes: 0
Reputation: 614
For someone who is looking for array of objects,
Sample array of objects
{
"month": "2020-05",
"start_date": "2020-05-01",
"end_date": "2020-05-31",
"invoices_count": 19,
"total_margin": 44420.83,
"total_revenue": 415826.48999999993
},
{
"month": "2020-10",
"start_date": "2020-10-01",
"end_date": "2020-10-31",
"invoices_count": 25,
"total_margin": 62583.130000000005,
"total_revenue": 553906.81
},
{
"month": "2020-09",
"start_date": "2020-09-01",
"end_date": "2020-09-30",
"invoices_count": 21,
"total_margin": 46459.35,
"total_revenue": 397549.66000000003
}]
The below is the code to get cumulative sum of total_margin for each object
data.map((item, index) =>
index === 0
? item
: {
...data[index],
'total_margin': data
.slice(0, index + 1)
.reduce((prev, curr) => prev + curr[total_margin], 0),
}
);
Expected output:
[
{
"month": "2020-05",
"start_date": "2020-05-01",
"end_date": "2020-05-31",
"invoices_count": 19,
"total_margin": 44420.83,
"total_revenue": 415826.48999999993
},
{
"month": "2020-10",
"start_date": "2020-10-01",
"end_date": "2020-10-31",
"invoices_count": 25,
"total_margin": 107003.96,
"total_revenue": 553906.81
},
{
"month": "2020-09",
"start_date": "2020-09-01",
"end_date": "2020-09-30",
"invoices_count": 21,
"total_margin": 153463.31,
"total_revenue": 397549.66000000003
}]
Upvotes: 0
Reputation: 147286
You could do this with reduce
, initialising with an array with [0]
in it and then appending the sum of the iterated and previous accumulator value in each loop, the cutting off the first value (with slice
) at the end:
const arr = [2, 1, 4, 3, 2]
const res = arr.reduce((acc, v, i) => acc.concat(acc[i] + v), [0]).slice(1)
console.log(res)
Upvotes: 0
Reputation: 65
Here is the simplest answer using a reducer.
let runningSum = function(nums) {
nums.reduce((pre, curr, i) => {
nums[i] = pre + curr;
return nums[i];
}, 0);
return nums;
};
console.log(runningSum([1,2,3,5,6,8]));
Runtime: 133 ms, faster than 5.02% of JavaScript online submissions for Running Sum of 1d Array. Memory Usage: 41.9 MB, less than 93.93% of JavaScript online submissions for Running Sum of 1d Array.
Upvotes: 0
Reputation: 1406
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));
Update 7.6.2022:
You can use the comma operator instead of currying to make an easier-to-read version with explicit sum = 0
.
console.log([5, 10, 3, 2].map((sum = 0, n => sum += n)));
Three years and no one thought to suggest this? :)
Upvotes: 95
Reputation: 79
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)
}
Upvotes: 0
Reputation: 351
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]));
Upvotes: 4
Reputation: 41
var nums= [5, 10, 3, 2];
var runningSum = function(nums) {
nums.reduce((acc, _, i) => (nums[i] += acc));
return nums;
};
Upvotes: 1
Reputation: 505
/*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));
Upvotes: 0
Reputation: 854
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)
Upvotes: 6
Reputation: 11
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]
Upvotes: 1
Reputation: 53
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.
Upvotes: 3
Reputation: 507
/**
* 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)
})
Upvotes: 0
Reputation: 2320
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;
}
Upvotes: 1
Reputation: 41
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]))
Upvotes: 2
Reputation: 11
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]
Upvotes: 1
Reputation: 1
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 ]
Upvotes: 0
Reputation: 303
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()
Upvotes: 10
Reputation: 2147
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)
https://jsfiddle.net/p31p877q/1/
Upvotes: 2
Reputation: 1615
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]
Upvotes: 12
Reputation: 54
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:
past_sums.slice(-1) === []
1*past_sums.slice(-1) === 0
cumsum
returns [past_sums
and new_sum
] as next cycle's past_sums
cumsum
returns [5, 15, 18, 20]
as the output Array some_sums
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, []);
Upvotes: 0
Reputation: 544
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,...}
Upvotes: 0
Reputation: 11
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 }, [] );
Upvotes: 1
Reputation: 20796
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]
Upvotes: 31
Reputation: 215059
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 })
Upvotes: 2
Reputation: 414086
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;
}, []);
Upvotes: 23
Reputation: 21852
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.
Upvotes: 5