user1735921 user1735921 - 1 month ago 13
Javascript Question

Javascript reduce function

Hello I have an array as follows:

[[1,1],[2,8],[3,12],[4,13],[5,23],[6,30],[7,41],[8,44],[9,50]]


So lets say it is the format of
[[x,y]]


As you can see the above array, the first element inside inner arrays goes from
1,2...6
which is x, and the second element of inner arrays are
1,8,12,13,23 and 30
which is y.
This is a cumulative array of y and I want to convert it into non-cumulative but with a twist.

I want to convert y to non-cumulative array by getting the difference from the value of y at last 3rd value of x

Therefore I want the final result to be as follows:

[[1,1],[2,8],[3,0],[4,1],[5,11],[6,0],[7,11],[8,14],[9,0]]


I have tried a fiddle here:
https://jsfiddle.net/gxytx6ka/1/

So far I have been able to get the difference between two array elements by some help of Dekel (stackoverflow guy) as you can see in the above fiddle:



$(document).ready(function() {
var old_arr = [ [1, 1], [2, 8], [3, 12], [4, 13], [5, 23], [6, 30], [7, 41], [8, 44], [9, 50] ];
var new_arr = old_arr.reduce(function(a, b, c, d) {
if (a.length) {
a.push([d[c][0], d[c][1] - d[c - 1][1]])
} else {
a = [b]
}
return a;
}, []);

console.log(new_arr);
});

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>





which outputs:

[[1,1],[2,7],[3,4],[4,1],[5,10],[6,7],[7,11],[8,3],[9,6]]


but I want:

[[1,1],[2,8],[3,0],[4,1],[5,11],[6,0],[7,11],[8,14],[9,0]]


which I am not able to get.

I tried using
a%3==0
in if condition, but not working...
Basically I want the final output in an efficient way.

Answer

With reduce you can do it like this (also using ES6 arrow function and spread operator):

var arr = [[1,1], [2,8], [3,12], [4,13], [5,23], [6,30], [7,41], [8,44], [9,50]];

var result = arr.reduce( ([arr, v], [x, y], i) =>
      [[...arr,[x, i%3==2 ? 0 : y-v]], i%3==2 ? y : v], [[],0])[0];

console.log(result);

Explanation

reduce is called with a double initial value (a pair):

[[],0]

The first element is the array that will accumulate into the final result, and the second value is the current value to which subsequent y values will be offset.

These two values are captured by the argument [arr, v] which is a destructuring assignment (ES6). The reduce callback will always return a new pair like that, with extended arr and (potentially) updated v.

The callback function also takes the current [x, y] pair from the original array. Again this is a destructuring assignment, so you have direct access to x and y variables. The third argument i is the current, zero-based index in the original array.

As said, the callback returns a pair. The first element in that pair is constructed as follows:

[...arr, [x, i%3==2 ? 0 : y-v]]

The ...arr notation spreads the previously collected result elements as a list, and one pair is added to that list: [x, i%3==2 ? 0 : y-v]. The x is just reproduced from the original [x, y], but the y follows the requested logic with the ternary operator: if we are at an index that has a remainder of 2 when divided by 3, then the y value should be 0. Otherwise, it should be offset against the previously set value v.

The second element in the pair, must be the new value for v:

i%3==2 ? y : v

Again, according to the requested logic, v remains unchanged when the remainder is not 2, but otherwise it is set to the current value of y.

So reduce will thus accumulate/update these two values in each iteration, and finally return a pair, of which only the first has our interest, which is why there is [0] at the end.

Notes

As you seemed to be looking for a reduce implementation, that is what I went with, but whenever your output array as just as many elements as your input array (like is the case here) you can also consider to use map as a good alternative (See answer that NinaScholz posted).

If you are open for a less functional programming way, you can also choose to use a for loop and maybe gain a bit of performance.