Why does my page fall into the infinite loop? - javascript

function randomNumber(){
var value;
var flag = false;
var tds = document.querySelectorAll('td');
do{
value = Math.round(Math.random() * (26 - 1) + 1);
for(var t = 0; t < tds.length; t++){
if(tds[t].innerHTML == value)
flag = true;
}
if(!flag){
return value;
}
}while(flag == true)
}
This function returns a random number for innerHTML of a new td. In case there are other tds with the same number as this code generates, the loop starts once again. If the generated number is unique, I add it to the innerHTML of a new td. But I can't even load the page since I run into an infinite loop, but no matter how hard I tried, I couldn't notice the problem in logic of this code.

As soon as your loop find the case where tds[t].innerHTML == value it sets flag to true - at this point you can never end the loop because nowhere do you check for a case where you can set flag to false, so your loop condition will always be true.
Here's a similar example that illustrated this with an array. You can see that sometimes it adds numbers to the array (in the case where it finds a new value) but other times the loop hits 5000 iterations an exits (because it never finds a new value), in which case it adds undefined to the array, since the function hasn't returned anything.
const arr = []
function randomNumber(){
var value;
var flag = false;
var tds = arr
var iterations = 0
do {
value = Math.round(Math.random() * (26 - 1) + 1);
for(var t = 0; t < tds.length; t++){
if(tds.includes(value))
flag = true;
}
if(!flag){
return value;
}
iterations += 1
console.log(iterations)
} while(flag == true && iterations < 5000)
}
for (let i = 0;i<20;i+=1) {
arr.push(randomNumber())
}
console.log(arr)

The moment your function at least once set the flag to true, its over - it never sets it to false again. To fix it i added one line of code.
function randomNumber(){
var value;
var flag = false;
var tds = document.querySelectorAll('td');
do {
flag = false; // this line i added
value = Math.round(Math.random() * (26 - 1) + 1);
for(var t = 0; t < tds.length; t++){
if(tds[t].innerHTML == value)
flag = true;
}
if(!flag){
return value;
}
}while(flag == true)
}
I will also write a bit more efficient code for you
function randomNumber(){
var value;
var found = false;
var tds = document.querySelectorAll('td');
var existingIds = [];
tds.forEach(td => existingIds.push(td.innerHHML)); // fill up the ids
do {
value = Math.round(Math.random() * (26 - 1) + 1); // this line would make problems (comment below)
if (existingIds.indexOf(value) === -1) found = true; // check if value can be found in existing ids and if found - set dount to true (you can also return from here, but i would rather user break (if there was more code after this line, than use retur in the middle of any loop;
} while(found === false)
return value;
}
Comment for line with random:
random() returns number from 0 to 1
as you wrote it - that value would be random number between 1 and 26 (only this values).
if all of the values are already used then our loop will be not ending (we could never find value between 1 and 26 that is not used, when all values from 1 to 26 are already used.
What can be done
You can add some counter (as #Ben did) and exit the loop in that case.
Or you can raise the number 26 to much higher
You can use consecutive numbers (get all, take the max one, add 1 and return this as new number)
You can of course find some other ways to counter that

Related

Optimizing node.js solution for HackerRank QHEAP1

Hi I'm trying to familiarize myself a bit better with Heaps so wanted to try and implement a solution to HackerRanks>Practice>Data Structures>Heaps>QHEAP1 using primitives, however I'm getting a timeout error for two of the tests.
A quick summary: I need to be able to parse a standardized input and handle the following 3 types of queries:
Add an element to the heap.
Delete a specific element from the heap.
Print the minimum of all the elements in the heap.
I'm wondering where this could be optimized? From what I can tell my del() will be performed in O(n) since I need to search for the element provided.
// search for and delete specific element {x} from heap
function del(arr, x){
let i = 0;
let found = false;
let n = arr.length;
while(!found && i < n){
if(arr[i] == x) found = true;
i++;
}
if(found){
arr[i-1] = arr[n-1]; // take the last element and overwrite to delete
arr.length = n - 1; // shorten array
downHeap(arr, i); // perform downHeap opertaion from index deleted
}
}
// NOTE: customized for minHeap due to requirement to print minimum value
function downHeap(arr, t){
// use array as binary tree - next index looking down is double current index
// NOTE: i and t are 1 indexed for heap lookahead
let i = 2 * t;
if(i >= arr.length) return; // no more room
// checkes if right child is smallest - if so updates index to right child
if(i < arr.length - 1 && arr[i - 1] > arr[i]) i = i + 1;
// if lower element is smaller than current element, swap em
if(arr[i-1] < arr[t-1]){
swap(arr, i-1, t-1);
downHeap(arr,i); // downHeap again at the next level
}
}
// insert x into heap
function insert(arr, x){
const n = arr.length;
arr.length = n + 1; // increasing array size
arr[n] = x; // adding el to end of array
upHeap(arr, arr.length)
}
//NOTE: customized as minHeap due to requirement to print minimum value.
function upHeap(arr, t){
// using array as binary tree - looking up - parant is half of current index
const i = Math.floor(t/2);
// if we've hit zero gone too far - NOTE: i, and t are 1 indexed for heap reference
// also nothing to do if parent is smaller than current index
if(i == 0 || arr[i-1] <= arr[t-1]) return;
// child is smaller than parent swap and upHeap from parent
swap(arr, t-1, i-1)
upHeap(arr, i)
}
// swahp
function swap(arr, l, r){
const t = arr[l];
arr[l] = arr[r];
arr[r] = t;
}
PS. as a side question, I'm kind of switching between a 1 indexed for heap operations, and a 0 index for array operations (e.g. you'll notices a lot of i-1 statements inside the up and downHeap methods) - wondering if there's a smarter way of having done that?
Support Code:
function processData(input) {
//Enter your code here
const inputs = input.split('\n');
const n = inputs[0];
let arr = [];
for(let i = 1; i <= n; i++){
const query = inputs[i].split(' ');
const op = query[0];
if(op == "1"){
insert(arr, parseInt(query[1]))
} else if(op == "2"){
del(arr, parseInt(query[1]))
} else if(op == "3"){
console.log(arr[0])
} else {
console.log("Error reading op");
}
}
}
process.stdin.resume();
process.stdin.setEncoding("ascii");
_input = "";
process.stdin.on("data", function (input) {
_input += input;
});
process.stdin.on("end", function () {
processData(_input);
});
Example Input
22
1 286789035
1 255653921
1 274310529
1 494521015
3
2 255653921
2 286789035
3
1 236295092
1 254828111
2 254828111
1 465995753
1 85886315
1 7959587
1 20842598
2 7959587
3
1 -51159108
3
2 -51159108
3
1 789534713
The code is indeed confusing because (as you write) it sometimes uses 1-based indexes, while other times it uses them as 0-based.
For instance, in insert, the following line shows that you intend t and i to be a 1-based index, since you convert them on-the-fly to a 0-based index:
if(arr[i-1] < arr[t-1])
...but then in this line, you treat i as a 0-based index (arr.length would be an admissible value of i if it is 1-based):
if(i >= arr.length) return; // no more room
And the same mix-up happens here:
if(i < arr.length - 1 && arr[i - 1] > arr[i]) i = i + 1;
By consequence you will get wrong results.
It is confusing to work with 1-based indexes when JavaScript is expecting 0-based indexes everywhere indexes are used. I didn't feel the courage to further debug your code in that state. I would suggest to use 0-based indexes throughout your code, which means that the left child of a value at index t is at index t*2+1.
Some other remarks:
To find the index where a value occurs in the heap, you don't have to write an explicit loop. Just use the built-in indexOf method.
Recursion is nice, but the downHeap and upHeap functions will work more efficiently with an iterative method, because then -- instead of swapping values -- you can take a copy of the value to bubble up or down, and then only move (not swap) the conflicting values to finally insert the copied value in its right place. This will perform fewer assignments than swapping repeatedly.
To insert a value you can just use the push method instead of updating the length "manually".
Instead of Math.floor for the integer division by 2, you can use a shift operator.
So here is a correction of your code:
function del(arr, x) {
const i = arr.indexOf(x); // This will be faster
if (i >= 0) {
const value = arr.pop();
if (i < arr.length) { // Only assign back when it was not last
arr[i] = value;
downHeap(arr, i);
}
}
}
function downHeap(arr, t) {
const val = arr[t];
while (true) {
let i = t * 2 + 1;
if (i < arr.length - 1 && arr[i] > arr[i + 1]) i = i + 1;
if (i >= arr.length || arr[i] >= val) break;
arr[t] = arr[i]; // Don't swap to gain time
// No recursion to save stack space
t = i;
}
arr[t] = val;
}
function insert(arr, x) {
arr.push(x); // adding element to end of array
upHeap(arr, arr.length - 1);
}
function upHeap(arr, t) {
const val = arr[t];
while (true) {
let i = (t - 1) >> 1; // Shift operator may give some speed increase
if (i < 0 || arr[i] <= val) break;
arr[t] = arr[i]; // Don't swap to gain time
// No recursion to save stack space
t = i;
}
arr[t] = val;
}

javascript while loop correctly iterating but for loop with same logic is not, on array with integer values and some null values in there

Iterating through a javascript array which has some data in, and some null or not defined values also, is giving funny behaviors with a for loop, but not with a while loop. It is not returning when it should and is stuck in an infinite loop
I have investigated the outputs extensively, the condition whether the number exists in the array is never evaluated to be true, only ever false, but it sometimes enters the if statement region as if it is true. It is seemingly arbitrary.
//function called within this code
function randomArrayOfIndexes() {
var randNumbArray = new Array(4);
var indexToAssign = Math.floor(Math.random() * Math.floor(4));
randNumbArray[0] = indexToAssign;
for (i = 1; i < randNumbArray.length; i++) {
indexToAssign = Math.floor(Math.random() * Math.floor(4));
while (arrayContains(randNumbArray, indexToAssign)) {
indexToAssign = Math.floor(Math.random() * Math.floor(4));
}
randNumbArray[i] = indexToAssign;
}
return randNumbArray;
}
//this works
function arrayContains(arrayin, numberIn) {
var i = arrayin.length;
while (i--) { //takes one from i so highest index is accurate on first iteration
if (arrayin[i] === numberIn) {
return true;
}
}
return false;
}
//this doesn't... not even backwards like the above iteration
function arrayIncludes(arrayin, numberIn) {
for (i = 0; i < arrayin.length; i++) {
if (arrayin[i] === numberIn) {
return true;
}
}
return false;
}
At first each function above is passed in an array with [int value, null, null, null], and a random number; when the function returns, the next null value is filled with the random number that doesn't exist in it already, so [int value, int value, null, null]... until all values are filled... the final array is filled with unique random numbers from 0 to 3, to provide an index for a piece of data in another array... to make sure that it is only used once in the program I am writing.
I would expect it to return true if the number passed in is already in there, another random number then generated outside of the broken function, and the process repeated until a unique random number is found. When it is found, the array being passed back in will be populated at the next available index, and the process repeated. This is not happening. It is getting stuck in an infinite loop, and never returning
you are just missing a var before i:
function arrayIncludes(arrayin, numberIn) {
for (var i = 0; i < arrayin.length; i++) {
// in ^ here
if (arrayin[i] === numberIn) {
return true;
}
}
return false;
}
You may also declare it before loop, like
var i;
for (i = 0; i < arrayin.length; i++) {
...
By the way, this way of generating random numbers without duplicates is very inefficient, I suggest something like having an array of 0-3 (in your current example) or 0-n and then just randomly taking items out of it. then you don't have to loop through the whole array each time you find a new number. every time you just find a random index between 0 and the length of remaining items.
Imagine that the array length is 1000, and the last item remaining is a number like 100, how many times you have to find a random number and loop through whole array till your random number is 100?
var n = 5;
var a = new Array(n);
for(var i=0;i<n;i++) a[i] = i;
var result = new Array(n);
var i = n;
while(i)
{
var index = Math.floor(Math.random() * i);
result[--i] = a[index];
a.splice(index,1);
}
document.getElementById('a').innerHTML = result;
<div id="a"></div>
You need to declare variables in you loops with for i=0. if you don't do this the variable is global and when you use the same loop variable in nested loops one can change the other.
You are using i in both loops so when you call the for loop with:
function arrayIncludes(arrayin, numberIn) {
for (i = 0; i < arrayin.length; i++) {
// etc
}
You set i back to 0 ad iterate it — this is the same i you are using in randomArrayOfIndexes so it interferes with that loop. This is a common cause of hard-to-find bugs and is hy you should always declare loop variables.
Here's the bug in it's simplest form. Notice that the out loop only runs once because i is incremented in the inner loop causing the outloop to exit early:
for (i = 0; i < 4; i++){
console.log("out loop number: ", i)
for (i = 0; i < 4; i++){
console.log("inner_loop: ", i)
}
}
If you declare the variables for for let i =, each loop gets its own version of i both loops run independently:
for (let i = 0; i < 4; i++){
console.log("out loop number: ", i)
for (let i = 0; i < 4; i++){
console.log("inner_loop: ", i)
}
}

Comparing values between two arrays

I'm trying to set up a function that checks if a word or a text is a palindrome. To do that, it splits the text so that every letter is an element of a new array, it takes rid of the white spaces and it makes the reverse array.
Then it checks if every element of the two arrays, at the same positions, are equal. If not it returns false, if yes it returns true.
Here the function:
function palindrome(str) {
var low = str.toLowerCase();
var newArray = low.split("");
var noSpace = newArray.filter(function(val) {
return val !== " ";
});
var reverse = noSpace.reverse();
function check (a, b) {
console.log(`checking '${a}' against '${b}'`);
var partial;
var result = 1;
for (var i = 0; i < a.length; i++) {
console.log(`comparing '${a[i]}' and '${b[i]}'`);
if (a[i] !== b[i]) {
result = 0;
} else {
partial = 1;
result *= partial;
}
}
return result;
}
var result = check(noSpace, reverse);
if (result == 1) {
return true;
} else {
return false;
}
}
palindrome("r y e");
I don't know what's wrong but it seems that the function keeps on returning a true value no matter what word or text I pass to the function. What is wrong with that?
Your issue seems to be because reverse() changes the actual array as well. So doing
var reverse = noSpace.reverse();
Will reverse noSpace and assign a reference to it on the variable reverse. That is, both arrays will be the same (reversed) array.
To bypass that, I've used .slice() to create a copy of the original array, and then called .reverse() on that new array, ridding you of any conflicts.
Here's a working snippet of what it looks like:
function palindrome(str) {
var str_array = str.toLowerCase().split("");
var no_space = str_array.filter(function(val) {
return val !== " ";
});
// By applying '.slice()', we create a new array
// reference which can then be reversed and assigned
// to the 'reverse' variable
var reverse = no_space.slice().reverse();
function check(a, b) {
var partial;
var result = 1;
for(var i=0; i < a.length; i++) {
if(a[i] !== b[i]) {
// We don't need to keep
// comparing the two, it
// already failed
return 0;
} else {
// I've kept this part even though
// I don't really know what it is
// intended for
partial = 1;
result *= partial;
}
}
return result;
}
return check(no_space, reverse) === 1;
}
console.log(palindrome("a b a"));
console.log(palindrome("r y e"));
The way you have coded for palindrome is way too complicated.
But there is one problem with your code: when you do a reverse() it changes the original array as well.
So you will need to make sure that you copy it via slice().
Also you can directly send a boolean result rather than doing a 1 and 0.
At result *= partial;, 1 * 1 will always equal 1
I didn't correct your code, but here is a optimized solution for you.
function palindrom(string) {
var arr = string.split("");
var lengthToCheck = Math.floor(arr.length / 2);
for (var i = 0; i < lengthToCheck; i++) {
if (arr[i] != arr[arr.length - (1 + i)]) {
return false;
}
}
return true;
}
First I split the array after every charater of the passed String. After that I get the half of the length of the array as it's enough to check just one half.
With the for-loop I compare the first half with the second half. As soon as I found two characters that do not match I return false. In case the whole first half matches the second half of the array, the for-loop will be completed and after that true will be returned.
What's actually happening is .reverse() reverses an array in place, it then stores a reference to that array which is not what you're calling in your check() method.
Simple fix would be to change your if statement:
if (a[i] !== b.reverse()[i])

Exit function and call itself again without returning undefined

I have the following function that can pull out a random index from an array without repeating the index and keeps pulling them out until all have been used and then resets itself and starts re-using them. It also tries to make sure that the last one that was pulled out isn't the same as the next one pulled out on the reset so that you don't ever have the same index come out in a row.
var listIndexes = [];
var lastIndex;
function getRandomIndex(indexes)
{
if (!listIndexes.length) {
for (var i = 0; i < indexes; i++) {
listIndexes.push(i);
}
}
var randomIndex = Math.floor(Math.random() * listIndexes.length);
var uniqueIndex = listIndexes[randomIndex];
listIndexes.splice(randomIndex, 1);
if(lastIndex && uniqueIndex == lastIndex)
{
listIndexes = [];
getRandomIndex(indexes);
return;
}
lastIndex = uniqueIndex;
return uniqueIndex;
}
var index = getRandomIndex(5);
console.log(index);
However when it hits the code: if(lastIndex && uniqueIndex == lastIndex) it causes it to return undefined for the index. So the way I'm trying to exit the function and re-call the function to try again isn't working as planned.
How can I exit the current function call and re-call the function to get a new random index that isn't the same as the lastIndex. Note that the lastIndex is kept intact until the new index isn't the same regardless of how many times the function is called.
Try this:
if(lastIndex && uniqueIndex == lastIndex)
{
listIndexes = [];
return getRandomIndex(indexes);
}
just change your empty return to run the function again:
return getRandomIndex(indexes);
The approach of rethrowing the die when you don't get the result you want is not optimal and when the random number isn't so random (tends to repeat the same number), then you're cycling unnecessarily.
Try this:
function RandEleGenerator(list) {
var lastChosen;
var currentList = list.slice();
function randomIndex() {
return Math.floor(Math.random() * currentList.length);
}
return function() {
// Choose element
var index = randomIndex();
var obj = currentList[index];
// Remove it from current list
currentList.splice(index, 1);
if(currentList.length == 0) {
// If empty, restore list
currentList = list.slice();
// But not without removing last chosen element
index = currentList.indexOf(obj);
currentList.splice(index, 1);
}
return obj;
};
}
Usage:
var reg = new RandEleGenerator([1,2,3]);
reg(); // 3
reg(); // 2
reg(); // 1
reg(); // 2
reg(); // 3
reg(); // 2
reg(); // 1
The chosen element is removed from the list so it cannot be rechosen. In order to guarantee that a value isn't repeated when the list ends, the list is recreated and the last element chosen is removed from the list immediately. The process then continues randomly choosing elements to remove from the list.
Edit:
In order to say, generate an array to pass to RandEleGenerator, the following code is sufficient:
var arrToPass = [];
for (var i = 0; i < 3; i++) {
arrToPass.push(i);
}
var reg = new RandEleGenerator(arrToPass);

how to check which elements of an array match relative to position

Trying to create a function or two that will be able to check the elements of an array and output wheater the elements of the two arrays are identical (ie same number and identical position is present), or the number is present but does not match the same position as the other array. Basically, I'm attempting to recreate a simple game called mastermind. The main problem im having is a case senarior when say the right answer is [1,2,3,4] and the user will guess [0,1,1,1], my function will out put that the number 1 is present 3 times, and I need to figure out how to just have it say the number 1 is present 1 time. Here is the function that checks the arrays:
function make_move(guess, answ){
var myguess = document.getElementById("mymoves");
var correct_number_correct_spot= 0;
var correct_number_wrong_spot= 0;
for(var i = 0; i < 4; ++i)
{
if(answ[i] == guess[i]){
++correct_number_correct_spot;
}
else if(answ[i] !== guess[i] && $.inArray(guess[i], answ) !== -1){
++correct_number_wrong_spot;
}
}
console.log(answ);
console.log(guess);
myguess.innerHTML += correct_number_correct_spot + " were right!" +correct_number_wrong_spot+ "there but not in the right order";
}
You can keep the count of missed numbers in an object, and subtract the guessed ones that appear in the answer. Then you can calculate the correct_number_wrong_spot subtracting the number of correct_number_correct_spot and the missed ones.
function make_move(guess, answ){
var myguess = document.getElementById("mymoves");
var correct_number_correct_spot = 0;
// Initialize missed counts to the numbers in the answer.
var correct_number_wrong_spot = answ.length;
var missed = {};
for (var j = 0; j < answ.length; j++) {
missed[answ[j]] = (missed[answ[j]] || 0) + 1;
}
for(var i = 0; i < answ.length; ++i)
{
if(answ[i] == guess[i]){
++correct_number_correct_spot;
}
// Subtract the guessed numbers from the missed counts.
if (guess[i] in missed) {
missed[guess[i]] = Math.max(0, missed[guess[i]] - 1);
}
}
// Subtract the correctly spotted numbers.
correct_number_wrong_spot -= correct_number_correct_spot;
// Subtract the remaining missed numbers.
for (var number in missed) {
correct_number_wrong_spot -= missed[number];
}
console.log(answ);
console.log(guess);
myguess.innerHTML += correct_number_correct_spot + " were right!" +correct_number_wrong_spot+ "there but not in the right order";
}
Check demo
EDIT: My try to explain doubts exposed in the comments:
would you mind explining how this code works: for (var j = 0; j < answ.length; j++) { missed[answ[j]] = (missed[answ[j]] || 0) + 1; }
missed[answ[j]] = (missed[answ[j]] || 0) + 1;
This is a quick way to increment the count for a number or initialize it to 0 if it doesn't exists yet. More or less the statement works like this:
If missed[answ[j]] is undefined then it is falsy and hence the || (or operator) evaluates to the 0. Otherwise, if we already have a value greater than 0, then it is truthy and the || evaluates to the contained number.
If it looks weird, you can replace this line with:
if (!(answ[j] in missed)) {
missed[answ[j]] = 0;
}
missed[answ[j]] += 1;
also if (guess[i] in missed) { missed[guess[i]] = Math.max(0, missed[guess[i]] - 1);
missed[guess[i]] = Math.max(0, missed[guess[i]] - 1);
In this case I use Math.max to make sure we don't subtract below 0. We don't want repeated numbers in the guess that exceeds the number of those present in the answer count. I mean, we subtract at most until the number of repeated numbers in the answer.
if (missed[guess[i]] > 0) {
missed[guess[i]] -= 1;
}
Try this fiddle!
Without changing your original function too much, you can use an object as a map to keep track of which numbers you have already matched.
var number_matched = {};
// ...
if(!number_matched[guess[i]]) {
number_matched[guess[i]] = true;
}

Categories

Resources