I have the following graph, which is a representation of an array [2,8,12,5,3,...]. X axis is in seconds. I want to break this array into multiple parts when y values stays 0 for longer than 2 seconds. So the array in this example would break into 3 parts: x = 0 to 8, x = 8 to 13 and x = 13 to 20 because y stays = 0 for more than 2 seconds from 8 to 13. In practice this array could be huge. What would be fastest method to do this in pure javascript (or if needed lodash/underscore)? Currently I am looping through this array to mark 2 second stop times. Is there a better way of doing this?
2 Answers
You could use an iterative approach with one loop while checking the expected zero value and decide if the threshold is reached or not. of not, then delete the last interval and append the length to the array before.
This proposal yields
with
threshold = 2:[ [ 1, 7], [ 8, 13], [14, 20] ]with
threshold = 7:[ [ 1, 20] ]
var y = [2, 8, 12, 5, 3, 2, 0, 0, 3, 4, 8, 10, 8, 10],
x = [1, 2, 4, 5, 6, 7, 8, 13, 14, 15, 16, 18, 19, 20],
threshold = 2,
isZero = false;
result = [];
y.forEach(function (a, i) {
var last = result[result.length - 1];
if ((a === 0) !== isZero) {
if (last) {
last[1] = x[i];
}
return;
}
isZero = !isZero;
if (last && isZero && x[i] - last[0] < threshold) {
result.pop();
if (result[result.length - 1]) {
result[result.length - 1][1] = x[i];
}
return;
}
result.push([x[i]]);
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
5 Comments
[ [ 1, 7], [ 8, 13], [14, 20] ] ? But let me check your solution carefully again.[ [ 1, 8 ], [ 8, 13 ], [ 13, 20 ] ] when it should be [ [ 1, 7], [ 8, 13], [14, 20] ]. I have made a fiddle based on your solution, could you please check it out? jsfiddle.net/7ymkqvLaYou'll always need to look at the values of the array, so you won't be able to get further than an O(n) solution. The most efficient would probably be to run through the array with a variable containing the amount of zeros you've passed through at a certain point.
The function below is a hastily made implementation of this. I've also used a variable to store the previous index. This could also be calculated from the split array, but that would be rather inefficient if you're really talking about huge arrays.
function splitArray(array, treshold) {
var zeros = 0,
previousIdx = 0,
splitArrays = [];
array.forEach(function(point, idx) {
if (point === 0) {
zeros++;
if (zeros == treshold && previousIdx != idx - treshold + 1) {
splitArrays.push(array.slice(previousIdx, idx - treshold + 1));
previousIdx = idx - treshold + 1;
}
} else if (zeros >= treshold) {
splitArrays.push(array.slice(previousIdx, idx));
previousIdx = idx;
zeros = 0;
}
});
if (previousIdx != array.length -1) {
splitArrays.push(array.slice(previousIdx));
}
return splitArrays;
}
I've created a JSFiddle that shows this function in action with some test data: https://jsfiddle.net/Glodenox/La8m3du4/2/
I don't doubt this code can still be improved though.
If you just want to get the indices of the sections instead of an array with all data in separate arrays, you can replace the three array.slice(a, b) statements with [a, b-1].
2 Comments
[{x:1,y:1}, {x:2,y:6}, ...], you'd just need to adjust point to point.x. Though you'd also need to up the zeros more than just one if there are gaps in the data.
[{x: 0, y: 2}, ..... ]