1

I need to perform a cumulative sum of an array. Under certain conditions, I need it to be straight forward, and found this snippet to work great:

cumul = sum.reduce(function (a, n) { 
                       a.push((a.length > 0 ? a[a.length-1] : 0) + n); 
                       return a;
                   }, [initial]);
cumul.shift();
Logger.log(cumul);

When I log 'cumul' I get the result I need. However, under other IF() conditions, I need to perform the cumulative sum providing a certain condition in another array containing dates is met - if the date is <= X, don't add to the cumulative sum in this iteration (show previous cumulative value).

Any ideas how to implement this? It seems that using this version of a cumulative sum won't work, but I'm not sure what would be other ways to go about this.

Thanks in advance! G

kuzzmi
  • 189
  • 1
  • 8
GimelG
  • 55
  • 8
  • My first suggestion is that you condense the two data structure into one. `var arr = [{val: 7, date: '2014-10-31'}, {val: 3, date: '2014-11-01'}]` then iterate through the one data structure. Since `javascript` arrays aren't really arrays there's no performance penalty to doing so. – Jared Smith Oct 31 '14 at 10:49
  • For non-conditional cumulative sum see http://stackoverflow.com/questions/20477177/creating-an-array-of-cumulative-sum-in-javascript -- some of the kinds of loop structures and helper functions might be reused. – Paul Oct 31 '14 at 11:05

2 Answers2

1

For loops are very fast in Javascript, and can create all kinds of conditional sums.

The function ccSum() below takes 2 parameters, a data array and a condition array. The data is summed if the condition is true.

function ccSum(data, condition){
   var r = [];
   r[0] = (condition[0])? data[0]: 0;  
   for(i=1,l=data.length; i<l; ++i) r[i] = r[i-1] + ( (condition[i])? data[i]: 0 );
   return r
}

It would be easy to rewrite this where condition is a function to be called, by changing the square bracket condition[i] for a round one condition(i). Alternatively, we could check typeof(condition)==='function' and branch these two cases.

Paul
  • 26,170
  • 12
  • 85
  • 119
1

You're making life far too hard on yourself with all that pushing and reducing and shifting.

function running_total(array, init) {
    return array
        .map(function(v, i) { return v ===3 ? 0 : v; }) // filter out 3's
        .map(function(v)    { return init += v; })
   ;
}

document.writeln(running_total([1,2,3,4,5], 0));

To make the filter pertain to another, parallel array of dates, change the first map function to something such as

function(v, i) { return dates[i] < cutoff ? 0 : v; }
  • This returns [1,3,3,7,12]. Is there a way for the function to skip values in a given iteration, but calculate it in the next one if it fits the criteria? For instance, in this example, I would want this to be the result: [1,3,3,10,15]. Thanks! – GimelG Oct 31 '14 at 14:51
  • Well of course there is a way, it's a computer, so you just program it to do whatever you want. You'd need another variable to keep track of the unadded values so you can add them back in later. –  Oct 31 '14 at 16:40
  • Right, I meant if there is a way using this method to achieve that :) Would appreciate a bit further guidance, fairly new with JavaScript. Thanks again. – GimelG Oct 31 '14 at 16:58
  • Add a new variable called "pending". For values which are filtered out, and set to zero, add them to that pending variable. Then, in the second map function, add back in pending if the value is non-zero (and reset it to zero). But what is the real-world situation that requires all this running around? –  Oct 31 '14 at 17:01
  • Can't quite make it work; when I just try to add it back in for !==0, it increments the first 2 values by 3 as well. The closest I can get is this: function running_total(array, init) { var pend = 0; return array .map(function(v) { return v ===3 ? (pend = v, 0) : v;}) .map(function(v) { return v === 0 ? (init += pend, pend = 0) : init +=v;}) ; } But this returns [1,3,0,10,15]. How do I make the 0 be the value from before? – GimelG Oct 31 '14 at 23:58
  • The real-world scenario is 2 columns of data; dates, and numbers. I need a cumulative sum offsetting x days, or in other words filtering values that are after date x. I have an array of days to offset from each date, and I need the cumulative sum to filter out the correct values in each iteration. – GimelG Nov 01 '14 at 00:03