Calculate highest occupancy over time period - javascript

Hi im doing a job at work where we have a camera system that pings everytime someone enters or exits a room so you would have data like this:
{enter:7, exit:6}
which should mean one person is left in room
I want to know what was the highest amount of people in the room over a given period lets just say 45 minutes. I could have 50 to 100 points of data how can i would out what the average would be
Thanks
// update with a code sample here
.then((results) => {
// maybe a correct algorithim
results.sort((a, b) => b.enter - a.enter);
results.sort((a, b) => b.exit - a.exit);
results.sort((a, b) => a.enter - b.enter || b.exit - a.exit);
// results.sort(fieldSorter(["enter"]));
let final_result = result.pop();
console.log(final_result);
if (final_result) {
return parseInt(final_result.enter - final_result.exit);
} else {
return 0;
}
})
So before this code im gathering all the datapoints then try and sort them and pop off the highest entertence to the lowest exits but i think im going about this wrong

Assuming that enter time is always smaller than exit time (which contradicts your example data), the issue could be solved like this:
function getAverage(startTime, endTime, items) {
let slots = {};
for (let timeIndex = startTime; timeIndex <= endTime; timeIndex++) {
slots[timeIndex] = 0;
}
for (item of items) {
let enter = Math.min(item.enter, startTime);
let exit = Math.min(item.exit, endTime);
for (let momentIndex = enter; momentIndex <= exit; momentIndex++) {
slots[momentIndex]++;
}
}
let avg = 0;
for (let slot of slots) avg += slot;
return avg / slots.length;
}
EDIT
For computing the maximum you can do something like this:
function getAverage(startTime, endTime, items) {
let slots = {};
for (let timeIndex = startTime; timeIndex <= endTime; timeIndex++) {
slots[timeIndex] = 0;
}
for (item of items) {
let enter = Math.min(item.enter, startTime);
let exit = Math.min(item.exit, endTime);
for (let momentIndex = enter; momentIndex <= exit; momentIndex++) {
slots[momentIndex]++;
}
}
let max = 0;
for (let slot of slots) max = Math.max(max, slot);
return max;
}

Related

Is there any way i can limit this for loop to a maximum of 50 loops?

This is a really unusual code i haven't written myself, each day a database is updated with around 200 records.
This means we have to do 200 api calls to figure out infomation about theese records every day at 7 am.
This has been working fine until now,
the api has implemented a limit of 50 api calls pr hour, and IF you try to do 51 calls, you get banned for 24 hours...
SO, how do i make the for loop do a maximum of 50 loops here?
for (let record of records ) {
//This loop has to be limited to 50 iterations pr run.
let brregRawData = await fetch(`some url/${record.name}/someurl`);
let brregJson = await brregRawData.json()
let personNavn = brregJson.rollegrupper[0].roller[0].person.navn.fornavn
let personEtternavn = brregJson.rollegrupper[0].roller[0].person.navn.etternavn
// if mellomnavn = undefined, then do nothing
if (brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn == undefined) {
var personMellomNavn = ""
} else {
let personMellomNavn = brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn + " "
}
I tried looking up different ways to use a for loop, but all the solutions i looked at broke the "${record.name}" part of the url, i have to loop through an entire view of an entire database.
I'm not sure if this is right for the case, but can you define a counter outside of the loop, increment internally and break when it reaches 50.
let count = 0;
for (....)
...
if (count++ > 50) break;
...
If I'm understanding you correctly, you need to limit the number of api calls to 50 but the current implementation uses a for ... of loop. The simplest way to achieve what you're looking for with the least amount of modification is to use a standard for loop.
Assuming this all happens inside an async function...
async function limitToFiftyApiCalls(records) {
const max = Math.min(records.length, 50);
for (let i = 0; i < max; i++) {
const record = records[i];
let brregRawData = await fetch(`some url/${record.name}/someurl`);
let brregJson = await brregRawData.json();
let personNavn = brregJson.rollegrupper[0].roller[0].person.navn.fornavn;
let personEtternavn = brregJson.rollegrupper[0].roller[0].person.navn.etternavn;
// if mellomnavn = undefined, then do nothing
if (brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn == undefined) {
var personMellomNavn = "";
} else {
let personMellomNavn = brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn + " ";
}
}
}
The code above doesn't modify your existing code much other than limiting the number of API calls. However there's a few things you could do that would generally make the code easier to read and edit.
async function limitToFiftyApiCalls(records) {
const max = Math.min(records.length, 50);
for (let i = 0; i < max; i++) {
const record = records[i];
let personMellomNavn = "";
let brregRawData = await fetch(`some url/${record.name}/someurl`);
let brregJson = await brregRawData.json();
// why write this more than once?
// save it to a variable and make life easier
let someVar = brregJson.rollegrupper[0].roller[0].person.navn;
let personNavn = someVar.fornavn;
let personEtternavn = someVar.etternavn;
if (someVar.mellomnavn) {
personMellomNavn = someVar.mellomnavn + ' '
}
}
}
A very simple way to do this is implementing a count that increments every time the loop executes the body. So for the the loop you provided it would look like this:
let count = 0; // initialize count as 0
for (let record of records ) {
if(count >= 50) break; // break out of loop if count is equal to or greater than 50 (since count starts from 0)
//This loop has to be limited to 50 iterations pr run.
let brregRawData = await fetch(`some_url/${record.name}/some_url`);
let brregJson = await brregRawData.json()
let personNavn = brregJson.rollegrupper[0].roller[0].person.navn.fornavn
let personEtternavn = brregJson.rollegrupper[0].roller[0].person.navn.etternavn
// if mellomnavn = undefined, then do nothing
if (brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn == undefined) {
var personMellomNavn = ""
} else {
let personMellomNavn = brregJson.rollegrupper[0].roller[0].person.navn.mellomnavn + " "
}
count++; // Increment the count after each iteration
}
Answer to you question:
for (let i=0; i<math.min(records.length, 50); i++ ) {
let record = records[i];
But what happens then with the records you have not checked? Will you wait 24h? I guess that's not what's expected, and instead will have to check 50 records every hour until you have checked them all (so 4 times for 200 records).

How to correctly move objects at different speeds in the direction of the `Y` axis

Tried to make a script like this:
let intervals = [],
isClick = [],
isGameOver = false,
countElement = 3,
count = 0,
gameOver = function () {
if (isGameOver) {
return;
}
isGameOver = true;
if (countElement <= count) {
for (var i = 0; i < intervals.legth; ++i) {
clearInterval(intervals[i]);
}
intervals = [];
countElement = 0;
}
},
elm = function (index) {
return function () {
if (isGameOver || isClick[index]) {
return null;
}
isClick[index] = true;
clearInterval(intervals[index]);
intervals[index] = null;
if (!intervals.filter(a => a).length) {
count = countElement;
gameOver();
return;
}
};
};
for (let i = 0; i < 17; ++i) {
setTimeout(() => {
element.on('pointerup', elm(i));
intervals[i] = setInterval(() => {
if (countElement <= count) {
clearInterval(intervals[i]);
gameOver();
return;
}
if (-64 > element.position.y) {
clearInterval(intervals[i]);
intervals[i] = null;
++count;
} else {
element.position.y -= 30;
}
}, pos.speed);
}, pos.startTime * i);
}
It actually works, but for some reason it doesn't always work as it should.
Perhaps I'll tell you right away what is required ..
It is necessary to generate the required number of elements and move along the axis Y.
They must have different speeds.
I tried to solve it like this:
let rnd = function (min, max) {
return Math.floor(Math.random() * (max - min) + min);
}, pos = {
speed: Math.floor(rnd(100, rnd(370, 470))),
startTime: Math.floor(rnd(rnd(370, 470), rnd(700, 1000)))
}
In general, I would like to see the elements start flying with different departure and flight speeds, there were attempts that can be missed if the element flew away, when you click on the element, it stops.
Well, in fact, if all the elements flew out - it doesn't matter if they flew away or stopped, the main thing is that all intervals should stop and there would be a kind of exit ...
Connoisseurs help out how this can be done without third-party libraries?
How to do it correctly, please show.
Problem solved...
There was no need to zero out countElement and intervals in gameOver.
Because of this, subsequent intervals were looped, as the indices were violated.

Optimizing process determining license usage in time

I am trying to find an efficient way to go through a big amount of data to determine how many units are processed at once.
So the data that I am receiving are just simple pairs:
{timestamp: *, solvetime: *}
What I need, is to see how many things are processed at each second.
To help you visualize what I mean: here is an example of data that I receive:
{{timestamp: 5, solvetime: 3},{timestamp: 7, solvetime: 5},{timestamp: 8, solvetime: 2},{timestamp: 12, solvetime: 10},{timestamp: 14, solvetime: 7}}
The chart below should help you understand how it looks in time:
https://i.stack.imgur.com/LEIhW.png
This is a simple case where the final calculation contains every second, but if the timeframe is much wider I show only 205 different times in this timeframe. E.g. if the time btw the first and the last timestamp is 20500 seconds I would calculate the usage for every second and divide the time into 205 parts - 100 seconds each and show only the second with the highest usage.
What I am doing right now is to iterate through all the pairs of input data and create a map of all the seconds, once I have it I go through this map again to find the highest usage in each time period (of 205 time periods I divide the whole time-frame in) and append it to the map of 205 timestamps.
It's working correctly, but it's very very slow and I feel like there is some better way to do it, a table might be faster but it is still not too efficient is it?
Here is the actual code that does it:
// results contain all the timestamps and solvetimes
// Timeframe of the chart
var start = Math.min.apply(Math, msgDetailsData.results.map((o) => { return o.timestamp; }))
var end = Math.max.apply(Math, msgDetailsData.results.map((o) => { return o.timestamp; }))
// map of all seconds in desired range (keys) the values are counter ofprocesses run in a given second
let mapOfSecondsInRange = new Map();
for (let i = start; i <= end; i++) {
mapOfSecondsInRange.set(i, 0);
}
// we go through every proces and add +1 to the value of each second in which the task was active
for (let element of msgDetailsData.results) {
var singleTaskStart = element.timestamp - Math.ceil(element.solveTime);
if (singleTaskStart < start) {
for (let i = singleTaskStart; i < start; i++) {
mapOfSecondsInRange.set(i, 0);
}
start = singleTaskStart;
}
for (let i = singleTaskStart; i < element.timestamp; i++) {
mapOfSecondsInRange.set(i, mapOfSecondsInRange.get(i) + 1);
}
}
// Preparation for the final map - all the seconds in the range divided into 205 parts.
const numberOfPointsOnChart = 205;
var numberOfSecondsForEachDataPoint = Math.floor((end - start) / numberOfPointsOnChart) + 1;
var leftoverSeconds = ((end - start) % numberOfPointsOnChart) + 1;
var highestUsageInGivenTimeframe = 0;
var timestampOfHighestUsage = 0;
let mapOfXXXDataPoints = new Map();
var currentElement = start;
for (let i = 0; i < numberOfPointsOnChart; i++) {
if (leftoverSeconds === 0) {
numberOfSecondsForEachDataPoint = numberOfSecondsForEachDataPoint - 1;
}
if (currentElement <= end) {
for (let j = 0; j < numberOfSecondsForEachDataPoint; j++) {
if (j === 0) {
highestUsageInGivenTimeframe = mapOfSecondsInRange.get(currentElement);
timestampOfHighestUsage = currentElement;
}
else {
if (mapOfSecondsInRange.get(currentElement) > highestUsageInGivenTimeframe) {
highestUsageInGivenTimeframe = mapOfSecondsInRange.get(currentElement);
timestampOfHighestUsage = currentElement;
}
}
currentElement = currentElement + 1;
}
mapOfXXXDataPoints.set(timestampOfHighestUsage, highestUsageInGivenTimeframe);
leftoverSeconds = leftoverSeconds - 1;
}
}

Get a total value from an array containing both strings ('A','J'...) and numbers (deck of cards in an array)

I have an issue where I have an array containing a deck of cards (['A', 2,3,...'J',...])
I want to be able to pick a number of random cards and then get the total sum of them. for example J,4 should give me the total value of 14.
my current problem is that I can't figure out how to change the strings in the array to a number and
then add those together to get the total sum.
my current code is:
blackjackGame={
'you': 0,
'cards': ['A','2','3','4','5','6','7','8','9','10','J','Q','K'],
'cardsMap' : {'A':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, '10':10, 'J':10, 'Q':10, 'K':10},
}
let playerCards = 2
let card = [];
const YOU = blackjackGame['you']
// gives me a random card
function randomCard (){
let rand = Math.floor(Math.random()* 13)
return blackjackGame['cards'][rand];
}
// gives me the two starting cards for the player in an array so I can later add more
function start(){
for(let i= 0; i < playerCards; i++){
card.push(randomCard())
}
return card
}
function totalValue (player){
// this is where i have no idea what to do
// let player = card.reduce(function (a,b){
// return a +b
// }, 0)
// return player += blackjackGame['cardsMap'][card[0]]
}
console.log(start())
console.log(showScore(YOU)) ```
PS. I'm trying to create a blackjack game.
Your reduce code is fine. Just add the reference to blackjackGame.cardsMap to retrieve the value that corresponds to card b.
let sum = card.reduce(function(a, b) {
return a + blackjackGame.cardsMap[b];
}, 0);
Note that you cannot return that value via the argument of the function. Instead let the function return it with a return statement:
return sum;
const blackjackGame={
'you': 0,
'cards': ['A','2','3','4','5','6','7','8','9','10','J','Q','K']
}
let playerCards = 2
let card = [];
const YOU = blackjackGame['you']
function getCardValue(card) {
const v = blackjackGame['cards']
if(v.indexOf(card) === -1){
throw Error("not found")
}
// for each card above index 9 (10), always return 10
return v.indexOf(card) > 9 ? 10 : v.indexOf(card) + 1
}
function randomCard (){
let rand = Math.floor(Math.random()* 13)
return blackjackGame['cards'][rand];
}
function deal(){
for(let i= 0; i < playerCards; i++){
card.push(randomCard())
}
return card
}
function calculateValue (cards){
return cards.reduce(function (total, num){
return total + getCardValue(num)
}, 0)
}
document.getElementById('deal').addEventListener('click',(e) => {
const cards = deal()
console.log(cards)
const playerValue = calculateValue(cards)
YOU = playerValue
console.log(playerValue)
})
<html>
<head>
</head>
<body>
<button id="deal">Deal</button>
<span id=cards />
<span id=total />
</body>
</html>
You need a way to map the face to the value. This will work:
function getValueOfCard( face ) {
var cardOrder =" A234567891JQK";
var faceStart = (""+face).substring(0,1);
return Math.min(10, cardOrder.indexOf(faceStart))
}
If you want to get the values of all your cards, simply iterate over them (faster than reduce, and more easy to read).
Your card only needs the face and color, the other values follow.
card = { color: "spades", face : "King" };
getValueOfCard( card.face );
function totalValue ( playerHand ){
// assuming an array of cards is the player hand
var total = 0;
for ( var card in playerHand ) {
total += getValueOfCard( card.face );
}
return total;
}
I also recommend, that you create all your cards in one go, and then shuffle them, by picking two random numbers and switching these two cards. Do this in a loop for a couple of times, and you have a randomized stack of cards, which means you can actually treat it as a stack.
cardColors = ["♠","♥","♦","♣"];
cardFaces = ['A','2','3','4','5','6','7','8','9','10','J','Q','K'];
// create deck of cards
var stackOfCards = [];
for ( var a = 0; a < cardColors.length; a++ ) {
var curColor = cardColors[a];
for ( var i = 0; i < cardFaces.length; i++) {
var curFace = cardFaces[i];
card = { color : curColor, face : curFace };
stackOfCards.push(card);
}
}
// shuffle the deck
// then you can pop cards from the stack to deal...
function start () {
for (let i = 0; i < playerCards; i++) {
cards.push(randomCard())
}
totalValue(cards)
}
function totalValue (cards) {
cards.forEach((card) => {
blackjackGame.you += blackjackGame.cardsMap[card]
})
}
start()
console.log(blackjackGame.you)
You were on the right track with having a map. You can access objects with a variable by using someObj[yourVar]

How would you throttle a recursive function?

Let's suppose I want to run a recursive function that will take weeks, months or even years to complete. It returns all possible permutations of a string based on the specified parameters. While it's running, I want to be able to see how far along it is progressing - e.g. how many permutations it has generated so far. In a nutshell, I want a very long-running recursive function to execute without locking up my UI.
Also, I would like to do this with vanilla ES5, not in strict mode, and without WebWorkers. It should be able to run in IE9.
What I have works fine as-is, but when I raise numspaces to 10, for example, the browser locks up. So I am assuming that I am just working the browser too hard, and "throttling" the amount of work it has to do would help solve this problem. I did try increasing the setTimeout delays from 1 to 250 and even 1000, but the browser still locked up.
I am interested in this simply because I tried to do it, and couldn't. Also, I know for a fact that this code is terribly inefficient and there are much, much better ways to do what I am looking to achieve. So recommend them!
var inputString = "abcdefghijklmnopqrstuvwxyz";
function allPossibleCombinations(input, length, curstr, callback) {
if (curstr.length === length) return callback(curstr);
(function(n) {
setTimeout(allPossibleCombinations.bind(n, input, length, curstr + input[n], callback), 1);
n++;
if (n < input.length) setTimeout(arguments.callee.bind(n,n), 1);
})(0);
}
var totalResults = 0,
numDigits = inputString.length,
numSpaces = 2,
maxResults = Math.pow(numDigits, numSpaces),
consoleElement = document.getElementById('console'),
startTime = +new Date();
console.log("Starting.. expecting", maxResults, "total results...");
allPossibleCombinations(inputString.split(""), numSpaces, "", function(result) {
totalResults++;
if (totalResults === maxResults) {
var elapsed = +new Date() - startTime;
consoleElement.innerText = "Done.";
console.log("Completed in", elapsed, "ms!");
} else {
// Do something with this permutation...
//...
// Show progress...
var progress = ((totalResults / maxResults) * 100).toFixed(2) * 1;
consoleElement.innerText = progress + "%";
}
});
<div id="console"></div>
You’re getting close with the setTimeout, but the current implementation queues up all the timers for a given prefix at once, resulting in an exponential number of timers and quick memory exhaustion. One small change would be to create another callback to indicate completion and use it to wait on recursive calls, never holding more than one timer at once:
var inputString = "abcdefghijklmnopqrstuvwxyz";
function allPossibleCombinations(input, length, curstr, resultCallback, doneCallback) {
if (curstr.length === length) {
resultCallback(curstr);
doneCallback();
return;
}
var n = 0;
(function next() {
if (n === input.length) {
doneCallback();
return;
}
allPossibleCombinations(
input, length, curstr + input[n],
resultCallback,
function () {
n++;
setTimeout(next, 0);
});
})();
}
var totalResults = 0,
numDigits = inputString.length,
numSpaces = 4,
maxResults = Math.pow(numDigits, numSpaces),
consoleElement = document.getElementById('console'),
startTime = +new Date();
console.log("Starting.. expecting", maxResults, "total results...");
allPossibleCombinations(
inputString.split(""), numSpaces, "",
function (result) {
totalResults++;
// Do something with this permutation...
//...
// Show progress...
var progress = ((totalResults / maxResults) * 100).toFixed(2) * 1;
consoleElement.innerText = progress + "%";
},
function () {
var elapsed = +new Date() - startTime;
consoleElement.innerText = "Done.";
console.log("Completed in", elapsed, "ms!");
});
<div id="console"></div>
That’s really slow, though. Thinking of how you could write this as a generator:
function* strings(input, length, current) {
if (current.length === length) {
yield current;
return;
}
for (let i = 0; i < input.length; i++) {
yield* strings(input, length, current + input[i]);
}
}
and translating that to a system where the callback is responsible for resuming generation:
function strings(input, length, current, yield_, continue_) {
if (current.length === length) {
yield_(current, continue_);
return;
}
var i = 0;
(function next() {
if (i === input.length) {
continue_();
return;
}
strings(input, length, current + input[i++], yield_, next);
})();
}
you can have the flexibility of setting a timer as infrequently as you’d like for performance.
"use strict";
function countSequences(n, k) {
var result = 1;
for (var i = 0; i < k; i++) {
result *= n--;
}
return result;
}
function strings(input, length, current, yield_, continue_) {
if (current.length === length) {
yield_(current, continue_);
return;
}
var i = 0;
(function next() {
if (i === input.length) {
continue_();
return;
}
var c = input[i++];
strings(input.replace(c, ''), length, current + c, yield_, next);
})();
}
var inputString = "abcdefghijklmnopqrstuvwxyz";
var totalResults = 0;
var numDigits = inputString.length;
var numSpaces = 5;
var maxResults = countSequences(numDigits, numSpaces);
var consoleElement = document.getElementById('console');
var startTime = +new Date();
console.log("Starting… expecting", maxResults, "total results.");
strings(
inputString, numSpaces, "",
function (result, continue_) {
if (totalResults++ % 1000 === 0) {
var progress = (totalResults / maxResults * 100).toFixed(2);
consoleElement.innerText = progress + "% (" + result + ")";
setTimeout(continue_, 0);
} else {
continue_();
}
},
function () {
var elapsed = +new Date() - startTime;
consoleElement.innerText = "Done.";
console.log("Completed in", elapsed, "ms!");
});
<div id="console"></div>
(This style is still non-optimal, but it’ll never finish for 2610 no matter how quick individual operations are.)

Categories