1

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.

15
  • 1
    Why are the y cumulative ? I don't get the logic between the initial array and the result Commented Nov 7, 2016 at 12:37
  • First a is an array. Second, have you tried (c+1)%3 === 0? Commented Nov 7, 2016 at 12:38
  • @Rajesh it is giving me [[8,44],[9,6]]. @Weedoze: It comes cumulative from backend, I want to do this calculation in javascript Commented Nov 7, 2016 at 12:41
  • 50-44 is 6 (for position 9). By what logic is that supposed to be 0? Commented Nov 7, 2016 at 12:44
  • 1
    I still don't understand what "every 3rd" means Commented Nov 7, 2016 at 12:46

4 Answers 4

8

Why reduce? A simple loop does the job perfectly fine:

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

let out = [],
    n = 0,
    lastY = 0;

for (let [x, y] of a) {
    if (++n % 3 == 0)
        lastY = y;
    out.push([x, y - lastY]);
}

console.log(JSON.stringify(out))

Sign up to request clarification or add additional context in comments.

4 Comments

As you seems to be the only one who understood the logic, could you please add it to your answer thus everyone can correctly understand the question and answer ?
Because I thought that reduce would be faster ?
reduce is not by definition faster -- although it can be. Reasons for using reduce are more often related to the coding style you prefer, and functional programming addicts love it. But it you want to go for speed, you'll often (not always) get a slight performance improvement with for loops.
@user1735921: reduce is slower and severely hurts readability. No reason to use it here.
3

You could use Array#map, which returns simply an array with the result.

var array = [[1,1], [2,8], [3,12], [4,13], [5,23], [6,30], [7,41], [8,44], [9,50]],
    result = array.map(function (a, i) {
        (i + 1) % 3 || (this.last = a[1]);
        return [a[0], a[1] - this.last];
    }, { last: 0 });

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

ES6

var array = [[1,1], [2,8], [3,12], [4,13], [5,23], [6,30], [7,41], [8,44], [9,50]],
    result = array.map((l => ([x, y], i) => (++i % 3 || (l = y), [x, y - l]))(0));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

4 Comments

Nice solution with good use of the comma-operator, @NinaScholz!
@trincot, looks a bit golfed. ;)
I had never seen .as-console-wrapper before, nice to know :) ... +1 anyways
Your first solution compiles successfully in ExecJS. Thanks a lot.
1

It is possible to do this with reduce — You just need to figure out when to zero-out every third value and when to find the difference between the current value and it's previous (third) value.

Naming parameters with more intelligible names also helps out.

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(result, value, index, arr) {
  if (index % 3 == 2) {
    return result.concat([[value[0], 0]]); // Set every third value to 0.
  } else {
    var prevIndex = index - (index % 3) - 1;
    var prevVal = prevIndex > -1 ? arr[prevIndex][1] : 0;
    return result.concat([[value[0], Math.abs(prevVal - value[1])]]);
  }
}, []);

console.log(JSON.stringify(new_arr)); // [[1,1],[2,8],[3,0],[4,1],[5,10],[6,0],[7,11],[8,3],[9,0]]

Comments

1

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 has 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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.