I have a script that notifies users every minute. I set an array of users for each minute.
I would like to create a script that limits the number of users for one minute and passes a user along to the next minute when the array is full.
I can create it stepwise like this (just a sample scheme of my script):
let min = 1
if (min.users.length > 10) {
let nextmin = min + 1
if (nextmin.users.length > 10) {
let nextnextmin = nextmin + 1
if (nextnextmin.users.length > 10) {
.....
} else {
// setting user for the next next min
}
} else {
// setting user for the next min
}
} else {
// setting user for the min
}
What I need is the script that checks on the next minute in a loop until it finds an array with a number of users under the set limit.
I hope I'm understanding your question correctly. Try the code below.
//usersArray = array that holds arrays of users per minute, maxUsers = limit of users per minute
function numUsersPerMin(usersArray,maxUsers){
for(let i = 0; i<userArray[i].length; i++) {
if(userArray[i].length < maxUsers){
return i; //I'm not sure what you want returned so I'm just returning the
//index of where the first array that is below maxUsers is
}
}
}
//i acts as the "minute"
Related
I am trying to create a Countup counter Starting from 1 to 10000 and i do not want it to reset when user refreshes the page or cancels the page. The Counter should start from 1 for every user that visits the page and keep running in background till it gets to 10000 even if the page is closed.
I have written the page below which;
Starts from the specified number for every new visitor
Saves the progress and does not reset when page is refreshed, however
It does not keep counting when page is closed and starts from the last progress when user closes the tab and comes back later. My code is
function countUp() {
var countEl = document.querySelector('.counter');
var countBar = document.querySelector('.progress-bar');
var x = parseInt(localStorage.getItem('lastCount')) - 1 || 1;
var y = countEl.dataset.to;
var z = countBar.dataset.to;
function addNum() {
countEl.innerHTML = x;
x += 1;
if (x > y && x > z) {
clearInterval(timer);
}
localStorage.setItem('lastCount', x);
}
var timer = window.setInterval(addNum, 1000);
localStorage.setItem("addNum", counter);
toggleBtn.addEventListener('click', function(){
countUp();
toggleBtn.classList.add('hidden');
});
}
countUp();</script>
<body onload=countUp();>
<div class="counter" data-from="0" data-to="10000000"></div>
<div class="progress-bar" data-from="0" data-to="10000000"></div>
</body>
It's difficult to show an example on StackOverflow because it doesn't let you fiddle with localStorage but, it sounds like you want something like:
When a user visits the page check localStorage for a timestamp.
If timestamp exists, go to step 4
Timestamp doesn't exist so get the current timestamp and stash it in localStorage.
Get the current timestamp. Subtract the timestamp from before. If over 10,000, stop, you're done.
Display difference calculated in step 4.
Start a 1 second timer, when time is up, go to step 4.
Something along those lines should work even if they refresh the page and since you are calculating from the original timestamp it will "count" in the background even if the page is closed.
window.addEventListener("DOMContentLoaded", () => {
const start = localStorage.getItem("timestamp") || Date.now();
localStorage.setItem("timestamp", start);
function tick() {
const now = Date.now();
const seconds = Math.floor((now - start) / 1000);
const display = document.getElementById("display");
if (seconds > 10000) return display.innerHTML = "We're done";
display.innerHTML = seconds;
setTimeout(tick, 1000);
}
tick();
});
<div id="display"></div>
So, client-side code can't normally execute when a client-side javascript page is closed.
What you could do, however, is calculate where the timer should be then next time it is loaded.
For example, in your addNum() function, you could in addition to the last count, also store the current date (and time).
function addNum() {
countEl.innerHTML = x;
x += 1;
if (x > y && x > z) {
clearInterval(timer);
}
localStorage.setItem('lastCount', x);
localStorage.setItem('lastDate', new Date());
}
Then, when your code starts, you can retrieve lastDate, and then subtract the current Date() from it.
Then use that to add the difference to your counter.
function countUp() {
let storedCount = parseInt(localStorage.getItem('lastCount'));
let storedDate = Date.parse(localStorage.getItem('lastDate'));
let now = new Date()
let diffSeconds = (now.getTime() - storedDate.getTime()) / 1000;
let storedCount += diffSeconds;
var countEl = document.querySelector('.counter');
var countBar = document.querySelector('.progress-bar');
var x = storedCount - 1 || 1;
var y = countEl.dataset.to;
var z = countBar.dataset.to;
}
I'm sure there are some more changes required to make it work with your code, but the idea is to store the current time so that when the page is closed and reopened, you can 'adjust' the count to catch up to what it should be.
What you want here is not possible just from the client-side code, there is no way for 2 different machines to share that information at all.
Here's the thing though, you can do this with a backend where the database gets updated every time a new IP hits the server. Note with this approach, a user here is one system and not different browsers or sessions.
To update this real-time for someone who is already on the website, run a timer and call an API that specifically gives you the count. Therefore the page gets updated frequently. You can also do this with react-query as it comes with inbuilt functions to do all this.
I need to display a "caption" from a video but I cannot use built in captions provided by a vtt file for my situation. I created an array that stores the key,value pair (time, caption). I traverse that array every time the video time is updated and find which caption fits the current time. This solution works and I haven't had any issues (performance or otherwise) but it just feels like brute force to me, I'm hoping someone can help me either refine what I have or guide me toward a more elegant solution. Any comments or criticism is appreciated.
//this function is called whenever video time is updated
this.onUpdateTime = function(currentTime, totalTime) {
this.currentTime = currentTime;
this.totalTime = totalTime;
/*the for statement traverses the array chapters which contains
[{"time": X,"caption": "XYZ"},...]*/
for (var i = 0; i < $scope.chapters.length; i++) {
/* the first if statement checks if (i is not the same as
chapters.length (this was done to prevent an off by one error) and the time of
the video is greater than or equal to time at chapters[i] and the time of the
video is still less than time at the next chapter marker chapter[i+1]*/
if (i != $scope.chapters.length - 1 && currentTime >= $scope.chapters[i].time && currentTime < $scope.chapters[i + 1].time) {
/*set the caption to the value from chapters[i].caption*/
$rootScope.caption = $scope.chapters[i].caption;
/*I have never used $scope.$apply but it seemed to be needed to get the caption to display in my view*/
$scope.$apply(); {
/*break the for loop so that it does not needlessly loop through the rest of the array after finding a match*/
break;
}
/*this if statement if for when i is equal to chapters.length -1, it is the final value in the array so it does
not check against the "next value" like the previous if statement does because there is not next value*/
} else if (currentTime >= $scope.chapters[i].time && i == $scope.chapters.length - 1) {
/*set the caption to the value from chapters[i].caption*/
$rootScope.caption = $scope.chapters[i].caption;
/*I have never used $scope.$apply but it seemed to be needed to get the caption to display in my view*/
$scope.$apply(); {
/*break the for loop so that it does not needlessly loop through the rest of the array after finding a match*/
break;
}
/*if there is not chapter marker at the current time*/
} else {
/*set the caption to a blank value because no match was found therefore there is no caption*/
$rootScope.caption = "";
$scope.$apply();
}
}
// console.log("onUpdateTime function, currentTime is: " + currentTime);
// console.log("onUpdateTime function, totalTime is: " + totalTime);
};
I'm using NodeJs.
I received constantly request from server.
I'm added some variable like createdTime to it and saved to the database.
when I sorted data by createdTime in some case It is not reliable, It is Repeated
How can I make differentiate between them ?
I do not want to count request.
I do not like to change timestamp's format.
var createdTime = new Date().getTime();
Here's a method of combining a counter with the current time to allow you to have as many as 1000 separate transactions within the same ms that are all uniquely numbered, but still a time-based value.
And, here's a working snippet to illustrate:
// this guarantees a unique time-based id
// as long as you don't have more than 1000
// requests in the same ms
var getTransactionID = (function() {
var lastTime, counter = 0;
return function() {
var now = Date.now();
if (now !== lastTime) {
lastTime = now;
counter = 0;
} else {
++counter;
}
return (now * 1000) + counter;
}
})();
for (var i = 0; i < 100; i++) {
document.write(getTransactionID() + "<br>");
}
If you want something that is likely to work across clusters, you can use process.hrtime() to use the high resolution timer instead of the counter and then make the id be a string that could be parsed into a relative time if needed. Since this requires node.js, I can't make a working snippet here in the browser, but here's the idea:
// this makes a unique time-based id
function getTransactionID () {
var now = Date.now();
var hrtime = process.hrtime();
return now + "." + ((hrtime[0] * 1e9) + hrtime[1]);
}
Due to my low rep I can't add a comment but it looks like you are needing to go beyond milliseconds.Maybe this stackoverflow question can help you
How to get a microtime in Node.js?
I have a small script and what I'm trying to do is to write one value from 'Sheet 1' to 'Sheet 2'. Wait for the results to load and compare the cells to see if it is above 10% or not. I have some =importhtml functions in the spreadsheet and it takes along time to load. I've tried sleep, utilities sleep, and flush. None have been working, maybe because I might be putting it in the wrong area..
function compareCells() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var listSheet = ss.getSheetByName('Stocks');
var dataSheet = ss.getSheetByName('Summary');
var listSheetLastRow = listSheet.getLastRow();
var currRow = 1;
for (i = 1; i <= listSheetLastRow; i++) {
if (listSheet.getRange(1, 3).getValue() == 'asd') {
var ticker = listSheet.getRange(currRow, 1).getValue();
dataSheet.getRange(5, 4).setValue(ticker);
var value1 = dataSheet.getRange(15, 4).getValue();
var value2 = dataSheet.getRange(22, 4).getValue();
SpreadsheetApp.flush();
if (value1 > 0.10 && value2 > 0.10) {
listSheet.getRange(currRow, 8).setValue('True');
listSheet.getRange(currRow, 9).setValue(value1);
listSheet.getRange(currRow, 10).setValue(value2);
} else {
listSheet.getRange(currRow, 8).setValue('False');
}
} else {
Browser.msgBox('Script aborted');
return null;
}
currRow++;
}
}
If it is not important that you use the =IMPORTHTML() function in your sheet, the easiest way to do this will be to use UrlFetchApp within Apps Script. Getting the data this way will cause your script to block until the HTML response is returned. You can also create a time-based trigger so your data is always fresh, and the user will not have to wait for the URL fetch when looking at your sheet.
Once you get the HTML response, you can do all of the same processing you'd do in Sheet1 within your script. If that won't work because you have complex processing in Sheet1, you can:
use UrlFetchpApp.fetch('http://sample.com/data.html') to retrieve your data
write the data to Sheet1
call SpreadsheetApp.flush() to force the write and whatever subsequent processing
proceed as per your example above
By handling these steps sequentially in your script you guarantee that your later steps don't happen before the data is present.
I had a similar problem but came up with a solution which uses a while loop which forces the script to wait until at least 1 extra column or 1 extra row has been added. So for this to work the formula needs to add data to at least one extra cell other than the one containing the formula, and it needs to extend the sheet's data range (number of rows or columns), for example by adding the formula to the end of the sheet, which looks like what you are doing. Every 0.5 seconds for 10 seconds it checks if extra cells have been added.
dataSheet.getRange(5, 4).setValue(ticker);
var wait = 0;
var timebetween = 500;
var timeout = 10000;
var lastRow = dataSheet.getLastRow();
var lastColumn = dataSheet.getLastColumn();
while (dataSheet.getLastColumn() <= lastColumn && dataSheet.getLastRow() <= lastRow){
Utilities.sleep(timebetween);
wait += timebetween;
if (wait >= timeout){
Logger.log('ERROR: Source data for ' + ticker + ' still empty after ' + timeout.toString() + ' seconds.');
throw new Error('Source data for ' + ticker + ' still empty after ' + timeout.toString() + ' seconds.');
}
}
In case if you are getting these two values (
var value1 = dataSheet.getRange(15, 4).getValue();
var value2 = dataSheet.getRange(22, 4).getValue();
) after the =importhtml call, you have to add sleep function before these two lines of code.
You also can have a loop until you get some values into the range from =importhtml call and add some sleep in the loop. Also note that as of April 2014 the limitation of script runtime is 6 minutes.
I also found this link which might be helpful.
Hope that helps!
I'm working on a schedule management system which loads a person's work schedule into an array, and then the user is allowed to "inject" a new shift within that day. There are a couple of rules the code must follow, of course, and they are:
There must be no gaps between existing shifts and the added item. This means the added item's end time must be equal to or greater than the first item's start time, and the added item's start time must be equal to or less than the last item's end time.
The added shift must overwrite any existing shifts which occur during it's time frame. Essentially, it needs to splice itself into the array, but I'm unsure of how to do so using the .splice() method.
Any affected time blocks must be truncated/compressed to accommodate the new item. For example, if there is an existing time block which is an hour in duration, and we inject a 10 minute block into that beginning 20 minutes in, it would result in the first 20 minute portion of the original block, followed by the 10 minute added item, followed by the remainder of the original block. Added items must also be able to overlap other time blocks as well. So if we add a three hour block that covers 4 other time blocks (or starts/stops within them!) it must overwrite each of those.
This seems like a simple enough task, because basically it's just splicing ranges, but I don't know how to splice ranges in JavaScript.
Here's the code I've written to implement these rules so far:
var obj = {
start: '',
end: '',
};
var objArray = [];
var tmpArray = [];
var finalArray = [];
objArray.push({start: "12:00", end: "12:45"});
objArray.push({start: "12:45", end: "1:00"});
objArray.push({start: "1:00", end: "1:30"});
objArray.push({start: "1:30", end: "2:30"});
// added object
obj.start = "12:00";
obj.end = "12:10";
for (var i = 0; i < objArray.length; i++) { tmpArray.push(objArray[i]); }
//tmpArray = objArray; // preserve the original array
objArray.push(obj);
console.clear();
console.log("%o", objArray);
objArray.sort(function(a, b) { var x = a.start; var y = b.start; return ((x < y) ? -1 : ((x > y) ? 1 : 0)); });
// sanity check
if (obj.start >= obj.end){
console.log('Time logic is invalid.');
return false;
}
if (obj.end < tmpArray[0].start) {
console.log('There is a gap before the first item.');
return false;
}
if (obj.start > tmpArray[tmpArray.length - 1].end){
console.log('There is a gap after the last item.');
return false;
}
// Now for the fun stuff...
for (var i = 0; i < objArray.length; i++){
var tmpobj = objArray[i];
if (tmpobj.start == obj.start && tmpobj.end == obj.end){ // find our inserted object
index = i;
console.log('<<< obj injected: %s - %s [%d] >>>', obj.start, obj.end, index);
if (index == 0){ // is first item, start time was less than first item's start time
finalArray.push(obj);
if (obj.end == tmpArray[0].start){
finalArray.push(tmpArray[0]); // item insertion was a complete prepend...
} else if (obj.end >= tmpArray[tmpArray.length - 1].end){
console.log('entire array is consumed'); // item covers entire shift...
} else {
// This code is reached when obj start time is before or equal to first item
console.log('obj <= tmpArray[0].start, end > tmpArray[0].start');
}
} else {
if (obj.start == tmpArray[tmpArray.length - 1].end){
console.log('Item added at end of shift');
finalArray.push(tmpArray[i - 1]);
finalArray.push(obj);
}
}
} else {
console.log('obj tested: %s - %s [%d]', tmpobj.start, tmpobj.end, i);
if (obj.start > tmpobj.end) {
finalArray.push(tmpobj);
}
if (obj.end < tmpobj.start) {
finalArray.push(tmpobj);
}
}
// now iterate through array and combine like terms (activity type)
}
for (var i = 0; i < finalArray.length; i++){
console.log('%d) s: %s, e: %s', i, finalArray[i].start, finalArray[i].end);
}
How do I splice ranges in JavaScript?
You could consider your time blocks in term of start and end separately. I conceptually think of it as a linked list, which you can implement as an Array. With a special requirement that each node in the list must follow the order of START_TIME -> END_TIME and never have 2 START_TIME or 2 END_TIME nodes adjacent to each other. And any in between (free time blocks) is also defined by a START_TIME and END_TIME node.
Sis START_TIME
E is END_TIME
The array of valid time blocks may look like this [S1, E1, S2, E2, S3 ,E3]
So then when coding the logic to insert new blocks of time, you find your desired block by going to the appropriate START_TIME node for your new timeblock, then "splice" by creating an END_TIME E* node and a new START_TIME S-NEW node marking the beginning of your new block.
[S1, E1 ,S2 ,E* ,S-NEW, E2, S3, E3]
Then you may define the ending time by creating END_TIME node if necessary (remove old END_TIME E2 and creating a "free" block),
[S1, E1, S2, E*, S-NEW, E-NEW, S-FREE, E-FREE, S3, E3]
or reuse the old END_TIME E2 node that you "spliced" earlier if they end at the same time
[S1, E1, S2, E*, S-NEW, E2, S3, E3]
The reason you're hurting your brain at the end is because your code is doing too much all at once. I suggest breaking it up into functions, here are some suggestions:
canStart(time)
canEnd(time)
insertBlock(startBlock, endBlock)
removeBlock(startBlock, endBlock)