how to get delay working with setInterval and clearInterval - javascript

I'm writing a snippet where JS displays all elements of an array as a counter, with random delay between beach of the steps in loop. However, clearInterval and setInterval don't seem to be working, as what I observe is that it keeps printing values forever in the browser console.
here's the code
var low = Math.floor(Math.random() * (80 - 65 + 1)) + 65;
var high = Math.floor(Math.random() * (95 - low + 1)) + low;
// generate array between low to high
heartbeats = Array.from( {length: 15}, () => Math.floor(Math.random() * (high - low + 1)) + low );
// sort it
heartbeats = heartbeats.sort((a,b) => a - b);
for (i in heartbeats){
var randomPause = Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000;
theLooper = setInterval( function () {
if (i == (heartbeats.length - 1) ) clearInterval(theLooper);
console.log("Random Pause Is :: " + randomPause);
console.log(heartbeats[i]);
name1.innerHTML = heartbeats[i];
},randomPause);
}
what I get instead of counter is a print of low then high instead of printing all the elements in the array. and console log keeps printing high forever

Problem:
In fact in your actual code you are not printing the values of the array after a respective delay one by one, you are just printing all the elements in a banch after the delay is passed.
And when you use setInterval the display won't stop, it will be displaying for ever.
Solution:
You need to use setTimeout, inside your loop, instead of setInterval, it will delay the instruction once, and make sure to increment the delay of the setTimeout function according to the iterated index use:
setTimeout(function() {
console.log("index: " + i + " & element: " + el);
}, i * 1000);
Demo:
This is a Demo snippet:
var low = Math.floor(Math.random() * (80 - 65 + 1)) + 65;
var high = Math.floor(Math.random() * (95 - low + 1)) + low;
// generate array between low to high
heartbeats = Array.from({
length: 15
}, () => Math.floor(Math.random() * (high - low + 1)) + low);
// sort it
heartbeats = heartbeats.sort((a, b) => a - b);
heartbeats.forEach(function(el, i) {
setTimeout(function() {
console.log("index: " + i + " & element: " + el);
}, i * 1000);
});

Problem
I think your variable scope is causing the problem
Solution
Change for(i in heartbeats) to for (let i in heartbeats), otherwise your i inside the setInterval callback will be always the last index of your array
Detailed doc can be found here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Related

Recursive Function - Square Root Calculation - Too much recursion

I am attempting to write a function which provides results similar to the JavaScript built-in math.sqrt function. I am using the trial and error method where the function guesses, and then subsequently refines the guess for the square root of the number the user enters into the program, until the correct value is successfully reached.
Currently a recursive function with an if.. else statement is used to perform the calculations, and adjust the guess until it reaches the user defined target number when squared.
The function provides the correct answer for 11, 22, 33, 44, 55, any number where the square root is not a decimal, among others. However it returns an error - too much recursion with other numbers such as 13, although the program gets very close to solving the problem - usually just .00000000000000x above or below the target number.
This function has gone through multiple iterations where I finally settled on using two arrays to store previous guesses, sorted into one or the other depending on whether they were too large or too small, and storing the latest guess from each array in a variable which can be used in calculating the next guess.
Previous iterations were reaching the error of too much recursion much earlier, and were only returning correct results for numbers where the correct answer was arrived at within the first run through of the function, otherwise my browser would crash.
// HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Function library example</title>
<style>
input {
font-size: 2em;
margin: 10px 1px 0;
}
</style>
</head>
<body>
<input class="numberInput" type="text">
<p></p>
// JavaScript
<script>
var input = document.querySelector('.numberInput');
var para = document.querySelector('p');
function findSquareRoot(num){
function find(guess, square, lastSmallGuess, lastLargeGuess){
var smallerArray = [];
var greaterArray = [];
if(square == num){
return guess;
} else if(square > num){
greaterArray.push(guess);
console.log('Last Small Guess : ' + lastSmallGuess); // shows guess in developer tools
console.log(' Last Large Guess : ' + lastLargeGuess); // shows guess in developer tools
console.log((((guess * 1) + (lastSmallGuess * 1)) / 2), (((guess * 1) + (lastSmallGuess * 1)) / 2) * (((guess * 1) + (lastSmallGuess * 1)) / 2), lastSmallGuess, greaterArray[greaterArray.length-1]); // shows calculation process in dev tools
return find((((guess * 1) + (lastSmallGuess * 1)) / 2), (((guess * 1) + (lastSmallGuess * 1)) / 2) * (((guess * 1) + (lastSmallGuess * 1)) / 2), lastSmallGuess, greaterArray[greaterArray.length-1]);
} else if(square < num){
smallerArray.push(guess);
console.log('Last Small Guess : ' + lastSmallGuess); // shows guess in developer tools
console.log(' Last Large Guess : ' + lastLargeGuess); // shows guess in developer tools
console.log((((guess * 1) + (lastLargeGuess * 1)) / 2), (((guess * 1) + (lastLargeGuess * 1)) / 2) * (((guess * 1) + (lastLargeGuess * 1)) / 2), smallerArray[smallerArray.length-1], lastLargeGuess); // shows calculation process in dev tools
return find((((guess * 1) + (lastLargeGuess * 1)) / 2), (((guess * 1) + (lastLargeGuess * 1)) / 2) * (((guess * 1) + (lastLargeGuess * 1)) / 2), smallerArray[smallerArray.length-1], lastLargeGuess);
}
}
return find((num * 1) / 2, ((num * 1) / 2) * ((num * 1) / 2), 0, 0);
}
input.onchange = function() {
var num = input.value;
if(isNaN(num)) {
para.textContent = 'You need to enter a number!';
} else {
para.textContent = num + ' square root is ' + findSquareRoot(num) + '. ' +
num + ' square root is ' + Math.sqrt(num) + '.';
}
}
</script>
</body>
</html>
I expect my function to consistently deliver the same answer as the built in math.sqrt function. Although even when the function causes the browser to crash and therefore does not return the result, it has arrived at an answer very close to the correct one, as shown in the console section of the developer tools. With other numbers it returns precisely the same result as math.sqrt
Thank you for taking the time to read my question.

How to generate a random number that is evenly divisible by another randomly generated number

I'm working on a simple math flashcards app using JavaScript and jQuery. The available operations are addition, subtraction, multiplication, and division, each of which have functions that use generateTop() and generateBottom() to assign values to HTML elements.
I'm having trouble figuring out how to use my division problem function to generate random numbers so that thetopNumber is evenly divisible by the bottomNumber. I'm using the following code to generate random numbers for each problem:
let topNumber = 0;
let bottomNumber = 0;
function generateTop(max, min){
topNumber = Math.floor(Math.random() * (max - min) + min);
return topNumber;
};
function generateBottom(max, min){
bottomNumber = Math.floor(Math.random() * (max - min) + min);
return bottomNumber;
};
Here is my current division problem generator:
function division(){
problemTop.html(generateTop(1, 144));
problemBottom.html(generateBottom(1, topNumber));
opSym.html("รท");
}
I can adjust the max and min to get a positive integer for addition, subtraction, and multiplication problems but am stuck on how to ensure that I get a positive integer for division problems. I've tried messing with some different loop ideas but haven't gotten anything to work. I want to keep min at 1 and max at 144.
I've checked SO for solutions but can only find how to generate random numbers divisible by one other explicit, hard-coded number. How can I adjust my division function so that the topNumber can be divided evenly by the bottomNumber?
Add a denomenator parameter to your generateTop function, and pass in the randomly generated "bottom" (denominator) number returned from generateBottom. Multiply the new random number created in generateTop by the denominator, and return that.
Just add a single if statement to make your function recursive:
function generateBottom(max, min) {
bottomNumber = Math.floor(Math.random() * (max - min) + min);
if (topNumber % bottomNumber) {
generateBottom(max, min);
}
return bottomNumber;
}
What this does is it divides topNumber and checks if a remainder exists. If there is a remainder, it means the two do not divide, so it is recalculated. Otherwise, it returns bottomNumber as per usual.
You can do like this :
var a = Math.floor(Math.random() * 10)
var b = a * Math.floor(Math.random() * 10)
var c = b/a;
document.write(b + " is divisible by " + a + " ( *" + c + ")" )
In a * b, b is always divisible by a.
let topNumber = 0;
let bottomNumber = 0;
function generateTop(max, min, a){
topNumber = a * Math.floor(Math.random() * (max - min) + min);
return topNumber + " is divisible by " + a + " ( *" + topNumber/a + ")";
};
function generateBottom(max, min){
bottomNumber = Math.floor(Math.random() * (max - min) + min);
return bottomNumber;
};
document.write(generateTop(10, 1, generateBottom(10, 1)))

Issue with Implementing a hit counter that records hits for last 5 min

I've encountered this problem and still trying to solve this problem:
You want to log the number of hits to a site.
Implement two functions,
log_hit() which gets called when a hit is registered, and
get_hits_in_last_five_minutes() which returns the total number of hits
in the last five minutes.
Assume that all timestamps come in increasing order.
My idea (attempt to solve the problem):
Datastructure: Array.
My Logic:
When log_hit() is called, we basically store the time in ms (Date().getTime() in ms) in the array and store the hits on the particular second in hashmap.
function Counter() {
this.map = {};
this.store = [];
}
Counter.prototype.log_hit = function() {
const d = new Date().getTime()/1000;
this.store.push(d);
this.map[d] = this.map[d] ? this.map[d]++ : 1;
}
Counter.prototype.get_hits_in_last_five_minutes = function() {
const fiveMin = 60 * 60; //seconds.
const initalPointer = new Date().getTime() / 1000 - fiveMin;
// Somehow retrieve the value and return it back
}
However, I don't think this is the most optimal way of solving it , if I wanted to extend the solution for hour or some other granularity.
How would I solve this kind of problem?
Using queue would be the right way to deal with this problem.
var HitCounter = function() {
this.queue = [];
};
/**
* Record a hit.
#param timestamp - The current timestamp (in seconds granularity).
* #param {number} timestamp
* #return {void}
*/
HitCounter.prototype.hit = function(timestamp) {
this.queue.push(timestamp);
};
/**
* Return the number of hits in the past 5 minutes.
#param timestamp - The current timestamp (in seconds granularity).
* #param {number} timestamp
* #return {number}
*/
HitCounter.prototype.getHits = function(timestamp) {
while(this.queue.length && this.queue[0] <= timestamp-300) {
this.queue.shift();
}
return this.queue.length;
};
const counter = new HitCounter();
counter.hit(1);
counter.hit(2);
counter.hit(3);
counter.getHits(4);
counter.hit(300);
counter.getHits(300);
console.log(counter.getHits(301)); // should output 3.
We need to exploit this fact-
Assume that all timestamps come in increasing order.
Algorithm:
We record every hit in an array and increase it's size gradually by 1.
To get the no. of hits in the last 5 minutes(excluding current hit), we do a binary search to get our answer since all timestamps come in increasing order, which would be sorted by default.
First, we do a binary search on the array to get the last valid upper bound index with respect to time t provided to get_hits_in_last_five_minutes().
Upper bound used is the limit till which we could use the hits to judge the no. of calls in the last 5 minutes. This is necessary because the problem statement says get_hits_in_last_five_minutes() which returns the total number of hits in the last five minutes. So, technically it means that it will be used as an API to check how many calls were made till 5 minutes prior to the parameter passed time t. It doesn't guarantee that the time t passed to this method will always be the last inserted timestamp in the counter. Due to this, we need to search for the upper bound in the array, i.e, till which index the hits stored in the array could be counted as valid for our answer.
Second, we do a binary search from 0 till upper_bound to get all the valid hits that were under last 5 minutes prior to t.
Space complexity: O(n) where n is the no. of hits registered.
Time Complexity:
O(log(n)) to search for the upper bound.
O(log(n)) to get the actual hits registered in last 5 minutes.
Total Complexity = O(log(n)) + O(log(n)) = 2 * O(log(n)) = O(log(n))
Note: I converted time t in to seconds while storing and searching.
Code:
function Counter() {
this.hits = [];
this.hits_size = 0;
}
Counter.prototype.log_hit = function(t) {
this.hits[this.hits_size++] = t * 60;
}
Counter.prototype.get_hits_in_last_five_minutes = function(t) {
if (this.hits_size < 2) return 0;
t *= 60;
var upper_bound = this.getUpperBound(t);
this.last_call_type = 2;
var low = 0,
high = upper_bound;
while (low <= high) {
var mid = low + parseInt((high - low) / 2);
if (this.hits[mid] > t - 300) high = mid - 1;
else if (this.hits[mid] < t - 300) low = mid + 1;
else return upper_bound - mid + 1;
}
return upper_bound - low + 1;
}
Counter.prototype.getUpperBound = function(t) {
var low = 0,
high = t > this.hits[this.hits_size - 1] ? this.hits_size - 1 : this.hits_size - 2;
var ans = 0;
while (low <= high) {
var mid = low + parseInt((high - low) / 2);
if (this.hits[mid] >= t) {
high = mid - 1;
} else {
ans = mid;
low = mid + 1;
}
}
if (high < 0) return -1;
return ans;
}
console.log("*****Counter 1******");
var c1 = new Counter();
c1.log_hit(1);
console.log("Registered, 1 = " + c1.get_hits_in_last_five_minutes(1));
c1.log_hit(2);
console.log("Registered, 2 = " + c1.get_hits_in_last_five_minutes(2));
c1.log_hit(3);
console.log("Registered, 3 = " + c1.get_hits_in_last_five_minutes(3));
c1.log_hit(4);
console.log("Registered, 4 = " + c1.get_hits_in_last_five_minutes(4));
c1.log_hit(5);
console.log("Registered, 5 = " + c1.get_hits_in_last_five_minutes(5));
c1.log_hit(6);
console.log("Registered, 6 = " + c1.get_hits_in_last_five_minutes(6));
c1.log_hit(7);
console.log("Registered, 7 = " + c1.get_hits_in_last_five_minutes(7));
c1.log_hit(8);
console.log("Registered, 8 = " + c1.get_hits_in_last_five_minutes(8));
c1.log_hit(9);
console.log("Registered, 9 = " + c1.get_hits_in_last_five_minutes(9));
c1.log_hit(10);
console.log("Registered, 10 = " + c1.get_hits_in_last_five_minutes(10));
console.log("*****Counter 2******");
var c2 = new Counter();
c2.log_hit(2);
console.log("Registered, 2 = " + c2.get_hits_in_last_five_minutes(2));
c2.log_hit(7);
console.log("Registered, 7 = " + c2.get_hits_in_last_five_minutes(7));
c2.log_hit(8);
console.log("Registered, 8 = " + c2.get_hits_in_last_five_minutes(8));
c2.log_hit(9);
console.log("Registered, 9 = " + c2.get_hits_in_last_five_minutes(9));
c2.log_hit(10);
console.log("Registered, 10 = " + c2.get_hits_in_last_five_minutes(10));
c2.log_hit(11);
console.log("Registered, 11 = " + c2.get_hits_in_last_five_minutes(11));
c2.log_hit(12);
console.log("Registered, 12 = " + c2.get_hits_in_last_five_minutes(12));
c2.log_hit(17);
console.log("Registered, 17 = " + c2.get_hits_in_last_five_minutes(17));
console.log("Unregistered, 18 = " + c2.get_hits_in_last_five_minutes(18));
c2.log_hit(19);
console.log("Registered, 19 = " + c2.get_hits_in_last_five_minutes(19));
console.log("Unregistered, 20 = " + c2.get_hits_in_last_five_minutes(20));
c2.log_hit(21);
console.log("Registered, 21 = " + c2.get_hits_in_last_five_minutes(21));
console.log("Unregistered, 6 = " + c2.get_hits_in_last_five_minutes(6));
console.log("Unregistered, 500 = " + c2.get_hits_in_last_five_minutes(500));
console.log("Unregistered, 15 = " + c2.get_hits_in_last_five_minutes(15));
console.log("Registered, 17 = " + c2.get_hits_in_last_five_minutes(17));
Why binary search?
You might wonder as to why not loop backwards and get all those values' count that are under t - 300. This way, it could be O(1) per call.
Note that our search space is just under t - 300. If this increases, for example, t - 9000 then our backward iteration also increases till 9000, and if the number of calls for get_hits_in_last_five_minutes() happens to be 10000 or more, then the complexity of just looping multiplies to the overall complexity. So, it could be
10000 calls to get_hits_in_last_five_minutes() * 9000
If we use the algorithm described above, it will be
10000 calls to get_hits_in_last_five_minutes() * log(n)
What if the calls are never ending(Infinite)?
This depends upon how we choose to use the get_hits_in_last_five_minutes() method.
If time t passed to the calls made to get_hits_in_last_five_minutes() will always be in non-decreasing manner, then we could truncate/remove hits from our storage.
To do that, we can again do a binary search to get the index of the maximum value from our storage that doesn't come under t - 300. After that, we could just do an array slice and reset this.hits_size to the new length.
We need to do this in log_hit() method and not get_hits_in_last_five_minutes() since time t passed to it need not necessarily be a part of our registered hits.
Using array slice could add up to the complexity a bit since it returns a shallow copy of the original array and it's complexity is O(N) where N is end - start. See array slice complexity. To avoid this, we can make a linked list and store data in it and use a map to store the nodes. This way, we could reset the list's head and make the trimming/truncating O(1). We will need to maintain a count of truncated nodes too as we can't reset the map with new values.
If the time t passed to the calls made to get_hits_in_last_five_minutes() for your website are in any random order, then we can't truncate anything since we need all data to give an answer. In that case, probably store data in the database. The good part is, you could just query your answer from DB and instead of doing computation in javascript.

JavaScript stops execution half way and evaluates wrong answer?, Console.log is correct

I didn't think this was possible until console.log(); shown me that whats happening is impossible.
I can't understand how this is possible it's like those variables are being modified before code execution finishes.
I got this JavaScript code with debugging in it.
It's wrapped in this.
$('#buyAmountInput').keyup(function () {
var buyAmount = parseFloat($(this).val());
var totalPrice = 0;
var max = $(this).attr("max");
var min = $(this).attr("min");
if (buyAmount != $(this).val()) {
if (isNaN(buyAmount)) {
buyAmount = 1;
$(this).val('');
} else {
$(this).val(buyAmount);
}
} else {
if (buyAmount > max) {
buyAmount = max;
$(this).val(buyAmount);
} else if (buyAmount < min) {
buyAmount = min;
//$(this).val(buyAmount);
}
}
totalPrice = buyAmount * unitPrice;
//lots of code trimmed off here.
largessAmount = Math.round(buyAmount * largessRule.rebate) / 100;
////
console.log("Buy amount " + buyAmount + " LargessRebate " + largessRule.rebate);
console.log("Total Price " + totalPrice);
console.log("Largess Amount " + largessAmount);
console.log("New rate " + Number(totalPrice / (buyAmount + largessAmount)).moneyFormat());
console.log("No .moneyFormat() New rate " + Number(totalPrice / (buyAmount + largessAmount)));
console.log("( " + totalPrice + " / ( " + buyAmount + " + " + largessAmount + " ))");
////
$('#unitPrice').html(Number(totalPrice / (buyAmount + largessAmount)).moneyFormat());
});
Debug looks like this
Buy amount 5000 LargessRebate 20
Total Price 4250
Largess Amount 1000
New rate 0.71
No .moneyFormat() New rate 0.7083333333333334
( 4250 / (5000 + 1000))
function fastKeyListener content_script.js:208
Buy amount 5000 LargessRebate 20
Total Price 4250
Largess Amount 1000
New rate 0.00 //<- What happened here
No .moneyFormat() New rate 0.00008499830003399932 //<- What happened here
( 4250 / (5000 + 1000)) //<- Third line executed this will give good rate..
Even if the variables are global and this code is in a keypress up jQuery callback function.
The variables are printed before they are executed by console.log() and they are correct but the answer is dead wrong.
Here is the moneyFormat() which I don't think could be the problem could it?
var effective_bit = -2;
Number.prototype.moneyFormat = function () {
var num = this;
sign = (num == (num = Math.abs(num)));
num = Math.floor(num * Math.pow(10, -effective_bit) + 0.50000000001);
cents = num % Math.pow(10, -effective_bit);
num = Math.floor(num / Math.pow(10, -effective_bit)).toString();
for (var i = 0; i < Math.floor((num.length - (1 + i)) / 3); i++)
num = num.substring(0, num.length - (4 * i + 3)) + ', ' + num.substring(num.length - (4 * i + 3));
if (effective_bit < 0) {
if (cents < 10 && effective_bit == '-2') cents = "0" + cents;
money = (((sign) ? '' : '-') + num + '.' + cents);
} else {
money = (((sign) ? '' : '-') + num);
}
return money;
};
I didn't post the whole code as it's very large, but you can see it live here
Just put into the Unit to buy of 4999, then scroll to 5000 it's all good.. try putting like 5001 or 50000 it will reset it to 5000 and give wrong answer in the process.
EDIT:
could of simplified the question to why does the console.log() functions evaluate incorrect answer if the same equation generated with the same variables just 1 line after in execution after gives correct answer, even on calculator.
Like some quantum going on here, bear with me there is nothing I could have done from 1 line to another line during that code-execution no breakpoints were set nothing plus those callbacks are functions generated in their own sandbox I believe so they are like ajax threads all working to complete sooner or later they all work separately from each other, so nothing working together here to mess it up. What you think could possibly happen here? some temporarily corruption or something?
This occurs sometimes when doing claulations using string variables.
Try converting the buyAmount and any variable that came from HTML to number before any calculation.
You can use the Number() function or parseFloat().
http://jsfiddle.net/rcdmk/63qas2kw/1/

Why does this Increment not work?

I'm having some problems with this code. My problem is that with the code below, it doesn't plus the detection-ratio text with the 'incr'. It just enters the incr, but doesn't plus.
This is my code.
(function loop() {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
var incr=Math.floor(Math.random()*6);
setTimeout(function() {
document.getElementById('detection-ratio').innerText = '0 / '+ ++incr;
loop();
}, rand);
}());
The 'detection-ratio' text looks like this as default:
0 / 0
Then, lets say 'incr' generates the number '3', then it should increase the last 0 with 3, so it would look like this:
0 / 3
Then, lets say it's going to generate a new 'incr', lets say '5'. Then it would look like this:
0 / 8
---> But right now, it doesn't do that. It just writes the 'incr' into the 'detection-ratio' without increasing it.
Hope this code would help you to get the expected output, let me know if something breaks. Also stop iteration once it reaches > 26
var incr = 0;
(function loop() {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
incr += Math.floor(Math.random()*6);
setTimeout(function() {
console.log('0 / '+ incr);
loop();
}, rand);
}());
Thanks for the explanation and patience.
I am assuming you are trying to append text to detection-ratio
if so you need to
document.getElementById('detection-ratio').innerText += '0 / '+ incr;
++ before a variable is a pre-increment operator, since you are generating random numbers i am assuming that is not actually what you want.
Since you're calling the loop recursively anyway, you may want to consider a more functional approach:
(function loop(startctr) {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
nextctr = startctr + Math.floor(Math.random()*6);
setTimeout(function() {
console.log('0 / '+ nextctr);
loop(nextctr);
}, rand);
}(0));

Categories