JavaScript Array - all index over 0 are empty after loop - javascript

I have an array of date/timestamps saved as seconds, I'm looking to count the number of items that are between two timestamps (all times are assumed to be at midnight). I've cut down a larger function to the section that the problem seems to reside, let me know if you need any additional code.
var fullDay = 86399;
var end = parseInt(dataArray[0]) + parseInt(fullDay);
for (var i = 0; i < numberOfDays; i++) {
dataSegments[i] = sensorEventTotal(dataArray, end, previousEnd);
previousEnd = end;
end = parseInt(end) + parseInt(fullDay);
}
console.log(dataSegments.toString());
SensorEventTotal:
function sensorEventTotal (dataArray, end, previousEnd){
var counter = 0;
$.each(dataArray, function(i, item) {
if (parseInt(item) < end && parseInt(item) > previousEnd) {
counter++;
}
});
previousEnd = end;
return counter;
}
What I'm trying to do is to take the first date/time stamp and add 24 hours (fullDay value is 24 hours in seconds), I'm then looking to use that "end time" as the start point for the next loop with another 24 hours added onto that and so on.
In the end I'd want an array where each index stores the number of occurrences for each day e.g. dataSegments = [23,123,32,34] - so 23 events on day one, 123 events on day two etc.
At the moment this is the result I'm getting for dataSegments:
115,0,0,0,0,0
EDIT:
Sample of data in dataArray:
1496077569,1496077568,1496077567,1496077564,1496077563,1496077562,1496072956
Full array:
1496077569,1496077568,1496077567,1496077564,1496077563,1496077562,1496072956,1496072955,1496072951,1496072950,1496072949,1496072948,1496072809,1496072805,1496072804,1496072803,1495815090,1495815089,1495815088,1495807282,1495807281,1495807280,1495807279,1495807277,1495807276,1495807275,1495807274,1495807273,1495807267,1495807266,1495807265,1495805409,1495805408,1495805407,1495805406,1495805381,1495805380,1495805379,1495803061,1495803060,1495803059,1495803059,1495803000,1495802999,1495802998,1495786283,1495786282,1495786281,1495728263,1495728262,1495728261,1495728258,1495728257,1495728256,1495727698,1495727697,1495727696,1495727695,1495727694,1495727693,1495727491,1495727490,1495727489,1495727486,1495727485,1495727484,1495724286,1495724285,1495724284,1495724279,1495724278,1495724277,1495720363,1495720358,1495720357,1495720356,1495719373,1495719372,1495719368,1495719367,1495719366,1495717302,1495717301,1495717299,1495717298,1495717297,1495717296,1495713310,1495713309,1495713308,1495713305,1495713304,1495713303,1495713303,1495707902,1495707901,1495707897,1495707896,1495707895,1495707615,1495707611,1495707610,1495707609,1495707608,1495704627,1495704626,1495704625,1495704623,1495704622,1495704621,1495704133,1495704132,1495704128,1495704127,1495704126

This is what I managed to come up with. I hope code is clear just from variable names alone, given that the logic is very similar to yours.
const SECONDS_IN_DAY = 24 * 3600;
let events = [1496077569,1496077568,1496077567,1496077564,1496077563,1496077562,1496072956,1496072955,1496072951,1496072950,1496072949,1496072948,1496072809,1496072805,1496072804,1496072803,1495815090,1495815089,1495815088,1495807282,1495807281,1495807280,1495807279,1495807277,1495807276,1495807275,1495807274,1495807273,1495807267,1495807266,1495807265,1495805409,1495805408,1495805407,1495805406,1495805381,1495805380,1495805379,1495803061,1495803060,1495803059,1495803059,1495803000,1495802999,1495802998,1495786283,1495786282,1495786281,1495728263,1495728262,1495728261,1495728258,1495728257,1495728256,1495727698,1495727697,1495727696,1495727695,1495727694,1495727693,1495727491,1495727490,1495727489,1495727486,1495727485,1495727484,1495724286,1495724285,1495724284,1495724279,1495724278,1495724277,1495720363,1495720358,1495720357,1495720356,1495719373,1495719372,1495719368,1495719367,1495719366,1495717302,1495717301,1495717299,1495717298,1495717297,1495717296,1495713310,1495713309,1495713308,1495713305,1495713304,1495713303,1495713303,1495707902,1495707901,1495707897,1495707896,1495707895,1495707615,1495707611,1495707610,1495707609,1495707608,1495704627,1495704626,1495704625,1495704623,1495704622,1495704621,1495704133,1495704132,1495704128,1495704127,1495704126];
events = events.reverse();
let midnight = events[0] - events[0] % SECONDS_IN_DAY; // midnight before the first event
const eventsPerDay = []; // results array
const nrDays = 7; // lets count events for one week
let daysCounted = 0, eventsChecked = 0;
while (daysCounted < nrDays) {
midnight += SECONDS_IN_DAY;
let currentEvent = events[eventsChecked];
let eventsInThisDay = 0;
while (currentEvent < midnight) {
eventsInThisDay++;
eventsChecked++;
currentEvent = events[eventsChecked];
}
eventsPerDay[daysCounted] = eventsInThisDay;
daysCounted++;
}
console.log(eventsPerDay);
Notice that I reverse the sample array before running my execution. That is because your sample starts at May 29 and ends at May 25, so it's running backwards in time.
I encourage you to try your own code on a reversed array, might very well be the case that your solution is correct.
If you do not want to reverse the array, you could "reverse the counting" by going from the latest midnight to the first midnight, subtracting 1 day on each iteration.

In my opininion (and coming example), you can do it a bit simpler. Also, I've noticed a little problem in your function: if your dataArray and number of days (let's call them N) were big, you would have to iterate over the data array N number of times. It could become inefficient. Luckily you can do it in one loop iteration:
let sensorEventTotal = (data, start, daysNum) => {
// We create array of length daysNum filled with 0's.
let days = new Array(daysNum);
days.fill(0);
for(let entry of data) {
// If below the start timestamp, continue loop.
if(entry < start) continue;
// We calculate which day it is.
let index = parseInt((entry - start) / fullDay);
// We check if the entry is not from days we do not count.
if(index < daysNum)
days[index]++;
}
return days;
}
Code with working examples: http://jsbin.com/lekiboruki/edit?js,console.
EDIT: You didn't mention if your dataArray is sorted. My answer would also work on unsorted arrays.

Related

Why is the split() method approx. 2x slower when the result is pushed to an array?

Consider the following code snippet:
function split() {
let time = 0;
let result = [];
for (let i = 0; i < 10 * 1000 * 1000; i++) {
let start = performance.now();
let words = "foo bar baz".split(" ");
let end = performance.now();
time += (end - start);
// result.push(words);
}
console.log(`Time needed: ${time}ms`);
}
The output of this code is something like:
Time needed: 2664ms.
However, with the line // result.push(words); uncommented, the output is something like:
Time needed: 4991ms.
That's approx. 2x slower (note that I'm measuring only the time needed by split(), but not push()). Can someone explain me why?
performance.now() does not have the necessary accuracy to measure what you're trying to measure. The browser deliberately returns an inaccurate number from performance.now() in order to combat exploits like spectre. So in both cases, the numbers you are seeing are not going to be accurate.
As for why you're getting different inaccurate numbers, here's my speculation. I'm guessing that most of the time, end - start is 0, because both timestamps are rounded to the same thing. So what you're really counting is the number of times where the rounding of the timestamp flips from one number to the next. Your first code might run through the loop 10 times, with 9 of them reporting 0ms, and 1 of them reporting 1ms. But then when you make the whole thing take longer, now it only takes 5 times through the loop for enough time to elapse that the rounding goes the other way. And so out of 10 times through the loop, 8 report 0ms and 2 report 1ms.
You are taking your time 10.000.000 of times in a loop and get a overall sum of 2665 milliseconds. If "calculate" the length of one loop iteration, it is 0.0002665 milliseconds, ie about 0.27 microseconds. That's way below the accuracy of performance.now, which according to the docs is 5 microseconds (ie more than 20 times as big). Thus what you are measuring is more or less random ...
And just for demonstration purposes (yes, the measurement is still inaccurate): Doing both variants 1000 times and suming up the duration for each run will sum up to more or less the same, because the inaccuracies will cancel out each other. BTW I ran this test 4 times, and 3 times, split2() was "faster" ...
function split1() {
let time = 0;
let result = [];
for (let i = 0; i < 10 * 1000; i++) {
let start = performance.now();
let words = "foo bar baz".split(" ");
let end = performance.now();
time += (end - start);
//result.push(words);
}
return time;
}
function split2() {
let time = 0;
let result = [];
for (let i = 0; i < 10 * 1000; i++) {
let start = performance.now();
let words = "foo bar baz".split(" ");
let end = performance.now();
time += (end - start);
result.push(words);
}
return time;
}
let total1 = 0;
for (let i = 0; i< 1000; i++)
total1 += split1();
let total2 = 0;
for (let i = 0; i< 1000; i++)
total2 += split2();
console.log(total1.toFixed(1), total2.toFixed(1));

Calculate number of data received in past 30 seconds

I have a websocket where i receive price of stock randomly like 300ms or sometimes 1 second.I want to calculate how many price I received in past 30 seconds only.
var arr = [];
function onReceive(price) {
var timestamp = Number(new Date());
arr[timestamp] = [];
arr[timestamp].push(price);
if (arrarr[timestamp].length > 1000) {
arr.shift();
}
}
Now I just want to count how many price is received in last 30 seconds , I cannot come up with any logic.
I tried something like slicing last 30 items in array and calculating difference between last time stamp and -30 timestamp , which tells me how much time it took to receive 30 price ticks ,but i dont know how to calculate how to find how many ticks received in past 30 seconds , any ideas please.thank you.
arr[timestamp][arr[timestamp].length-1].key-arr[timestamp][0].key;
Personally I would create some sort of named instance for a log item, holding the UNIX timestamp and the price.
To retrieve anything in the last X seconds, you'd get the current UNIX timestamp, subtract X * 1000 from it, and use .filter() do a reverse iteration to retrieve all items where the timestamp is greater than that.
EDIT: As Robby pointed out, there's no need to search through the entire array as the timestamps are guaranteed to be in increasing order. By iterating in reverse, we can exit the loop when we find the first result outside of the desired window.
var priceLog = [];
function PriceLogItem(price) {
this.price = price;
this.timestamp = new Date().getTime();
}
function onReceive(price) {
priceLog.push(new PriceLogItem(price));
if (priceLog.length > 1000) log.shift();
}
function getPriceLogsSince(secondsAgo) {
let millisecondsAgo = secondsAgo * 1000;
let time = new Date().getTime() - millisecondsAgo;
let result = [];
for (let i = priceLog.length - 1; i >= 0; i--) {
if (priceLog[i].timestamp >= time) result.push(priceLog[i]);
else break;
}
return result;
}
//Usage
let priceLogs = getPriceLogsSince(30); //Get all logs within past 30 seconds
console.log(priceLogs);

Determining time remaining until bus departs

For our digital signage system, I'd like to show how long until the next bus departs. I've built the array that holds all the times and successfully (maybe not elegantly or efficiently) gotten it to change all that to show how much time is remaining (positive or negative) until each listed departure.
I need a nudge in the right direction as to how to determine which bus is next based on the current time. If there is a bus in 7 minutes, I only need to display that one, not the next one that leaves in 20 minutes.
I was thinking perhaps a for loop that looks at the array of remaining times and stops the first time it gets to a positive value. I'm concerned that may cause issues that I'm not considering.
Any assistance would be greatly appreciated.
UPDATE: Unfortunately, all the solutions provided were throwing errors on our signage system. I suspect it is running some limited version of Javascript, but thats beyond me. However, the different solutions were extremely helpful just in getting me to think of another approach. I think I've finally come on one, as this seems to be working. I'm going to let it run over the holiday and check it on Monday. Thanks again!
var shuttleOrange = ["09:01", "09:37", "10:03", "10:29", "10:55", "11:21", "11:47", "12:13", "12:39", "13:05", "13:31", "13:57", "14:23", "14:49", "15:25", "15:51", "16:17", "16:57", "17:37", "18:17"];
var hFirst = shuttleOrange[0].slice(0,2);
var mFirst = shuttleOrange[0].slice(3,5);
var hLast = shuttleOrange[shuttleOrange.length-1].slice(0,2);
var mLast = shuttleOrange[shuttleOrange.length-1].slice(3,5);
var theTime = new Date();
var runFirst = new Date();
var runLast = new Date();
runFirst.setHours(hFirst,mFirst,0);
runLast.setHours(hLast,mLast,0);
if ((runFirst - theTime) >= (30*60*1000)) {
return "The first Orange Shuttle will depart PCN at " + shuttleOrange[0] + "."
} else if (theTime >= runLast) {
return "Orange Shuttle Service has ended for the day."
} else {
for(var i=0, l=shuttleOrange.length; i<l; i++)
{
var h = shuttleOrange[i].slice(0,2);
var m = shuttleOrange[i].slice(3,5);
var departPCN = new Date();
departPCN.setHours(h,m,0);
shuttleOrange[i] = departPCN;
}
for(var i=shuttleOrange.length-1; i--;)
{
//var theTime = new Date();
if (shuttleOrange[i] < theTime) shuttleOrange.splice(i,1)
}
var timeRem = Math.floor((shuttleOrange[0] - theTime)/1000/60);
if (timeRem >= 2) {
return "Departing in " + timeRem + " minutes."
} else if (timeRem > 0 && timeRem < 2) {
return "Departing in " + timeRem + " minute."
} else {
return "Departing now."
}
}
You only need to search once to find the index of the next scheduled time. Then as each time elapses, increment the index to get the next time. Once you're at the end of the array, start again.
A sample is below, most code is setup and helpers. It creates a dummy schedule for every two minutes from 5 minutes ago, then updates the message. Of course you can get a lot more sophisticated, e.g. show a warning when it's in the last few minutes, etc. But this shows the general idea.
window.addEventListener('DOMContentLoaded', function() {
// Return time formatted as HH:mm
function getHHmm(d) {
return `${('0'+d.getHours()).slice(-2)}:${('0'+d.getMinutes()).slice(-2)}`;
}
var sched = ["09:01", "09:37", "10:03", "10:29", "10:55", "11:21", "11:47",
"12:13", "12:39", "13:05", "13:31", "13:57", "14:23", "14:49",
"15:25", "15:51", "16:17", "16:57", "17:37", "18:17","21:09"];
var msg = '';
var msgEl = document.getElementById('alertInfo');
var time = getHHmm(new Date());
var index = 0;
// Set index to next scheduled time, stop if reach end of schedule
while (time.localeCompare(sched[index]) > 0 && index < sched.length) {
++index;
}
function showNextBus(){
var time = getHHmm(new Date());
var schedTime;
// If run out of times, next scheduled time must be the first one tomorrow
if (index == sched.length && time.localeCompare(sched[index - 1]) > 0) {
msg = `Current time: ${time} - Next bus: ${sched[0]} tomorrow`;
// Otherwise, show next scheduled time today
} else {
// Fix index if rolled over a day
index = index % sched.length;
schedTime = sched[index];
msg = `Current time: ${time} - Next bus: ${schedTime}`;
if (schedTime == time) msg += ' DEPARTING!!';
// Increment index if gone past this scheduled time
index += time.localeCompare(schedTime) > 0? 1 : 0;
}
msgEl.textContent = msg;
// Update message each second
// The could be smarter, using setInterval to schedule running at say 95%
// of the time to the next sched time, but never more than twice a second
setInterval(showNextBus, 1000);
}
showNextBus();
}, false);
<div id="alertInfo"></div>
Edit
You're right, I didn't allow for the case where the current time is after all the scheduled times on the first running. Fixed. I also changed all the string comparisons to use localeCompare, which I think is more robust. Hopefully the comments are sufficient.
I have used filter for all shuttle left after the right time and calculated how much time left for the first one.
var shuttleOrange = ["09:01", "09:37", "10:03", "10:29", "10:55", "11:21", "11:47", "12:13", "12:39", "13:05", "13:31", "13:57", "14:23", "14:49", "15:25", "15:51", "16:17", "16:57", "17:37", "18:17"];
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var remainShuttle = shuttleOrange.filter(bus => bus.substring(0,2) > h || (bus.substring(0,2) == h && bus.substring(3,5) > m));
var leftMinutes = (parseInt(remainShuttle[0].substring(0,2))*60 + parseInt(remainShuttle[0].substring(3,5)) - (parseInt(h) *60 + parseInt(m)));
console.log(parseInt(leftMinutes / 60) + " hours and " + leftMinutes % 60 +" minutes left for next shuttle");

Comparing current and next element of array and returning time difference

This is my array. Its length is about 9000. This is what a small bit of it looks like:
foreach_arr = ["21:07:01.535", "21:07:01.535", "21:07:26.113"]
There are a few occurences where the times diff is greater than a minute, and that is when I want to grab those times. And later use those times to get certain indices from another array. i.e "array"
I'm also using moment.js for time parsing.
Expected result: array = [8127, 9375, 13166, 14182]
Actual result: array = [8127, 13166]
Can't seem to find the issue here, I am getting 2 results when im supposed to be getting 4.
If the whole array is needed for troubleshooting, ill add it if I can.
var xx = foreach_arr.length - 1;
for(var z = 0; z < xx; z++) {
var current_row = foreach_arr[z];
var next_row = foreach_arr[z + 1];
var msElapsedTime = moment(next_row,"HH:mm:ss.SSS").diff(moment(current_row, "HH:mm:ss.SSS")) / 1000;
if(msElapsedTime > 60) {
attempt_indices.push(foreach_arr[z]);
}
}
for(var x = 0; x < attempt_indices.length; x++) {
array.push(newdata.indexOf(attempt_indices[x]));
}
Since the OP doesn't really need my code anymore, I'm posting it here to remove the downvote as much as anything else :)
const foreach_arr = ["21:07:01.535", "21:07:01.535", "21:07:26.113", '22:01:01.000'];
let processedForeach_arr = [];
let gtOneMinuteDiff = [];
foreach_arr.forEach((elem1, index1) => {
// elem1.split(':') turns foreach_arr[0] into ['21', '07', '01.535']
const splitElementArray = elem1.split(':');
let timeInMs = 0;
// this changes ['21', '07', '01.535'] into [75600000, 420000, 1535]
splitElementArray.forEach((elem2, index2) => {
if (index2 === 0) { // elem2 is hours. 3.6M ms per hour.
timeInMs += parseFloat(elem2) * 60 * 60 * 1000;
} else if (index2 === 1) { // elem2 is minutes. 60K ms per minute.
timeInMs += parseFloat(elem2) * 60 * 1000;
} else if (index2 === 2) { // elem2 is seconds. 1K ms per second.
timeInMs += parseFloat(elem2) * 1000;
} else {
throw `Expected array element formatted like HH:MM:SS.ms. Error on
element ${elem1}.`;
}
});
processedForeach_arr.push(timeInMs);
let timeDiff = processedForeach_arr[index1 - 1] - processedForeach_arr[index1];
if (Math.abs(timeDiff) > 60000) {
gtOneMinuteDiff.push(timeDiff);
}
});
To get the difference in milliseconds between foreach_arr[n] and foreach_arr[n+1], this code will
split each element of foreach_arr into 3 strings (hours, minutes, and seconds + milliseconds)
run parseFloat on each of those values to convert them to a number
convert all numbers to milliseconds and add them together
compare each consecutive value and return the difference.
Ok, I got this far and my son needs me. I'll finish out the code asap but you might beat me to it, hopefully the instructions above help.
turns out my code wasn't wrong. Just my idea of the whole proccess.
array = [8127, 13166]
is what I initialy get. With this, I use indexOf on my other array to eventually get my array as expected:
var another_test_arr = [];
for(var v = 0; v < array.length ; v++) {
var find = foreach_arr.indexOf(attempt_indices[v]);
another_test_arr.push(array[v], newdata.indexOf(foreach_arr[find + 1]));
}
Result: array = [8127, 9375, 13166, 14182]

How to reduce the number of loops for checking if an array of values is in between another array of ranges

I'm trying to check if a time is within an array of intervals, I know how to do the comparisons...etc However I am worried about efficiency becuase I need to check a lot of time entries (6,000,000) and I'm using a service that provides a limited amount of processing time.
Right now I have an array of 48 time ranges, it's a 24 hour day split into 30 minute intervals. I loop through this array with each of the 6000k entries to see which one the entry falls in between. So I need to perform 288,000,000 loops with 2 conditional checks per loop.
So this is pretty much O(n*48).
In what ways can this be improved? This is in JavaScript.
Edit Code as requested:
Example interval:
Start: new Date(0,0,0,1,30,0)
End: new Date(0,0,0,2,0,1)
Example Time: (I turn the time string into a date as shown in the second bullet)
'2015-12-03 15:25:00'
new Date(0,0,0,15,25,0)
Example Loop:
for(var i = 0; i < myArray.length; i++)
{
for(var ii = 0; ii < myIntervals.length; ii++)
{
if(myArray[i] >= myIntervals[ii].start && myArray[i] <= myIntervals[ii].end)
{
myIntervalCounts[ii] ++;
}
}
}
From what a understood on the problem, you have 6kk entries in the form "11:37 AM","10:00 PM" and 48 time splits of 30 min each, and you one label them (for instance, 11:37 AM is in the 23rd label, with 0 indexing).
I believe need only one loop over the time entries:
function label_timestamp(hour,minute,period)
return hour*2 + (period==="PM")*24 + (minute>30)*1 )
for (e in entries)
console.log(label_timestamp(e.hour, e.minute, e.period)
This way 11:37 AM becomes 11*2+0*24+1*1=23
This is O(n), but again, the question is not very clear.
well, if your time-ranges line up, you could do sth like this:
var start = myIntervals[0].start,
end = myIntervals[myIntervals.length-1].end,
segment = (end-start) / myIntervals.length;
for(var i=0, j=myArray.length; i<j; ++i){
var v = myArray[i];
if(v < start || v >= end) continue;
var ii = ((v - start)/segment) >>> 0;
myIntervalCounts[ii]++;
}
assuming that the intervals are sorted, otherwise you would need to add some mapping

Categories

Resources