59

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?

Daniel Buckmaster
  • 7,108
  • 6
  • 39
  • 57
Shonna
  • 993
  • 2
  • 9
  • 12

29 Answers29

83

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? :)

JollyJoker
  • 1,256
  • 8
  • 12
  • 2
    @olefrank From the start, sum is 0. cumulativeSum is called with 5, so `sum += 5` adds 5 to `sum` and returns `sum`. Then it's called with 10, so `sum` becomes 15. Then 3, 18 and 2, 20. – JollyJoker Oct 15 '19 at 13:53
  • 1
    I'm still perplexed by the way that `cumulativeSum` works. How does `map` know to update `sum` each iteration? How does `sum` even survive its being assigned the argument (0)? (Fwiw, it still works with no argument in place of 0.) Is this arrow function black magic? – JohnK May 20 '20 at 23:41
  • 6
    Nice and short, but you have to be aware that you can run it only once, since `sum` keeps its value, and the next time you call `cumulativeSum` it will have retained its old value. So, works only repeatedly if you have `cumulativeSum` in some local scope and use it only once therein... – Elmar Zander May 26 '20 at 22:49
  • @ElmarZander Yeah, it would be better to just inline the code instead of declaring a function to make sure it won't be reused. – JollyJoker May 27 '20 at 08:28
  • 2
    Or define `cumulativeSum` as `const cumulativeSum = (sum => value => sum += value);` and then call it with `array.map(cumulativeSum(0));` – Kyle Barron Jun 10 '20 at 04:37
  • You have to declare this function everywhere you want the cum sum. It maintains some state internally. – Souradeep Nanda Aug 09 '20 at 14:28
31

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]
Matt
  • 20,108
  • 1
  • 57
  • 70
22

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;
}, []);
Pointy
  • 405,095
  • 59
  • 585
  • 614
13

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]
Paul Lucas
  • 1,302
  • 1
  • 12
  • 18
  • Beware when using spread in a reduce: the spread operator has to recreate the entire array every time it is run, so this call will be more inefficient the longer the array is. If your array is longer than a few hundred elements, this method is likely to cause significant slowdowns. Check out [this article](https://jpcamara.com/2023/03/07/making-tanstack-table.html) for more info. – Jacob Apr 10 '23 at 19:13
9

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

Shablcu
  • 295
  • 3
  • 11
5

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)
Roy Ashbrook
  • 814
  • 8
  • 14
4

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.

Sachin Jain
  • 21,353
  • 33
  • 103
  • 168
4

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]));
Flynn
  • 181
  • 4
3

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.

2

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 })
georg
  • 211,518
  • 52
  • 313
  • 390
2

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/

Prashant Tapase
  • 2,132
  • 2
  • 25
  • 34
2

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]))
Jibwa
  • 41
  • 1
1

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 }, [] );
Ed Brown
  • 11
  • 1
1

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]
Vichu Menon
  • 11
  • 1
  • 1
1

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;
}
p8ul
  • 2,212
  • 19
  • 19
1

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]
fnb
  • 11
  • 1
1
var nums= [5, 10, 3, 2];
var runningSum = function(nums) {
   
    nums.reduce((acc, _, i) => (nums[i] += acc));
    return nums;
};
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
1

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.

agnivesh
  • 115
  • 1
  • 2
  • 10
1

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);
nupanick
  • 754
  • 8
  • 13
  • 1
    Not to mention that the spread operator is O(N), so for the reduce call gets more expensive each time it runs (especially for large arrays) – Jacob Apr 10 '23 at 19:20
0

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,...}
Flavio
  • 506
  • 4
  • 9
0

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, []);
Community
  • 1
  • 1
0

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 ]
Taeho Oh
  • 1
  • 1
  • 1
    A direct solution is welcome, but please ensure you add context around the link so your fellow users will have some idea what it is – Pratibha Jan 26 '18 at 03:00
0
/**
 * 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)
})
Anson C
  • 507
  • 7
  • 18
0

/*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));
0

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)
}
SnazzyPencil
  • 79
  • 1
  • 13
0

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.

0

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)
Nick
  • 138,499
  • 22
  • 57
  • 95
0

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
    }]
Rahimuddin
  • 458
  • 5
  • 15
0

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);
Ateeb Asif
  • 94
  • 3