Creating an array of cumulative sum in javascript

JavascriptArrays

Javascript 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()

Arrow function

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)

https://jsfiddle.net/p31p877q/1/

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 and new_sum] as next cycle's past_sums
  • The last cycle:
    • cumsum returns [5, 15, 18, 20] as the output Array some_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)
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionShonnaView Question on Stackoverflow
Solution 1 - JavascriptJollyJokerView Answer on Stackoverflow
Solution 2 - JavascriptMattView Answer on Stackoverflow
Solution 3 - JavascriptPointyView Answer on Stackoverflow
Solution 4 - JavascriptPaul LucasView Answer on Stackoverflow
Solution 5 - JavascriptShablcuView Answer on Stackoverflow
Solution 6 - JavascriptRoy AshbrookView Answer on Stackoverflow
Solution 7 - JavascriptSachinView Answer on Stackoverflow
Solution 8 - JavascriptRahat DhandeView Answer on Stackoverflow
Solution 9 - JavascriptNotaru NguyenView Answer on Stackoverflow
Solution 10 - JavascriptJibwaView Answer on Stackoverflow
Solution 11 - JavascriptgeorgView Answer on Stackoverflow
Solution 12 - JavascriptEd BrownView Answer on Stackoverflow
Solution 13 - JavascriptPrashant TapaseView Answer on Stackoverflow
Solution 14 - JavascriptVichu MenonView Answer on Stackoverflow
Solution 15 - Javascriptp8ulView Answer on Stackoverflow
Solution 16 - JavascriptFlavioView Answer on Stackoverflow
Solution 17 - JavascriptJohn HofferView Answer on Stackoverflow
Solution 18 - JavascriptTaeho OhView Answer on Stackoverflow
Solution 19 - JavascriptAnson CView Answer on Stackoverflow
Solution 20 - JavascriptfnbView Answer on Stackoverflow
Solution 21 - JavascriptDon-Pierre HalfawayView Answer on Stackoverflow
Solution 22 - Javascriptuser1555785View Answer on Stackoverflow
Solution 23 - JavascriptAC1556View Answer on Stackoverflow