How to restart a loop in Javascript whilst decrementing a global variable - javascript

I want the if statement (line 10) to detect when the next string in the array (line 1) is not matching the string in index0 (outputString0). And then to decrement the variable testLength (line 11) to slice the last character off the strings to allow them to be compared again in the next loop iteration to determine whether they are still matching. Then I want the loop to restart by setting i back to 0, which will increment to 1 once the loop is re-started (line 12).
But it seems to create an infinite loop. Is it because I am trying to modify the global variable testLength (line 11) from within the if statement?
const wordInput = ['fasts', 'fasta', 'fasts', 'fasts', 'fasts'];
let testLength = 5;
let outputString0 = wordInput[0].toString();
outputString0 = outputString0.slice(0, testLength);
for (let i = 1; i < 5; i++) {
output = wordInput[i].toString();
output = output.slice(0, testLength);
if ((this['outputString' + i] = output) !== outputString0) {
testLength = testLength - 1;
i = 0; continue;
}
else {
this['testString' + i] = output;
continue;
}
};
I attempted to move the global variable testLength to inside the for loop, but the console also returns an error. Not sure what to do next.

Related

Parsing loops in a javascript interpreter

I've been exploring writing a very basic / limited interpreter in javascript as an exercise. All has been going well until I introduced the concept of LOOPs.
Given the following script:
LOOP 2
A
LOOP 3
B
END
LOOP 4
C
LOOP 5
D
END
E
END
F
END
The algorithm should visit the inner tokens in the following sequence:
ABBBCDDDDDECDDDDDECDDDDDECDDDDDEFABBBCDDDDDECDDDDDECDDDDDECDDDDDEF
The following does the trick, but it requires lots of iterating over the tokens. It's an improvement over a previous slicing approach I used that manually expanded the loops, but is far from optimal.
/**
* In practice, we'll grab each token as we read the script,
* but to keep this simple and focus on the loop algorithm,
* we can cheat and make an array of all the tokens.
*/
const getTokens = (s) => s.replace(/[\W_]+/g, " ").split(" ").filter(Boolean);
/* Temp vars - ideally, I'd like to solve this with arrays. */
const start = []; // Loop start indices
const end = []; // Loop end indices
const counts = []; // Times to loop
const completed = []; // Loops completed
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (token === "LOOP") {
if (start.length == 0 || i > start[start.length - 1]) {
// Add new loop index if we haven't seen it before
start.push(i); // Store the loop index
counts.push(Number(tokens[i + 1])); // The loop count is always next LOOP token
completed.push(0); // Initialize with 0 completed at index
// Find the end index for the loop
// Note: This is the slowest part.
let skip = 0;
for (let j = i + 2; j < tokens.length; j++) {
if (tokens[j] == "LOOP") {
skip++; // Increase nest depth
} else if (tokens[j] == "END") {
if (skip == 0) {
end.push(j); // Found matching loop close
break;
}
skip--;
}
}
}
i++; // Skip over the loop count
continue;
} else if (token === "END") {
let j;
for (j = 0; j < end.length; j++) {
if (end[j] == i) break; // Found matching end index
}
const isCompleted = completed[j] == counts[j] - 1;
if (!isCompleted) {
i = start[j] + 1;
completed[j]++;
for (let k = j + 1; k < start.length; k++) {
completed[k] = 0; // Reset nested loops in between
}
}
continue;
}
console.log(tokens[i]);
}
https://jsfiddle.net/5wpa8t4n/
What's a better way to accomplish this array-based approach using a single pass through the script, or at worst 2 passes, but not N-LOOP passes?
You don't need to know the position of the matching end of the loop when starting to interpret it. All you need to record is the position to jump back to when encountering the next end, but until then just continue interpreting token by token.
These positions, together with the respective counters, can be stored in a stack structure.
const script = `
DO
A
DO
B
LOOP 3
DO
C
DO
D
LOOP 5
E
LOOP 4
F
LOOP 2
`
const parse = (script) =>
script
.replace(/[\W_]+/g, " ")
.split(" ")
.filter(Boolean);
const interpret = (code) => {
let loops = []; // Active loops: iteration count and jump target
let ip = 0; // instruction pointer
let result = "";
while (ip < code.length) {
const instruction = code[ip];
switch (instruction) {
case "DO": {
++ip;
loops.push({count: 0, start: ip});
} break;
case "LOOP": {
const limit = Number(code[++ip]);
const {count, start} = loops.pop();
if (count < limit) {
loops.push({count: count+1, start});
ip = start; // jump back
} else {
++ip;
}
} break;
default: {
++ip;
result += instruction; // a print statement
} break;
}
}
return result;
};
console.log(interpret(parse(script)));
I've simplified the structure a bit to use do-while loops, so I'd never have to skip the loop body. In a true byte code, emitted by a parser, the jump targets (both back and forth) would be part of the instructions themselves, and only the count "variables" would need to be stored in the stack. The jump targets never change so you'd need to generate them only once in the parse function.

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;
}

Why does my page fall into the infinite loop?

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

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)
}
}

js Bubble sort not processing all elements correctly

I pass in elements array [5,4,3]. The bubble sort manages to push 5 the end, this is fine, but when it loops through a third time to locate the second to last element, the loop breaks and the incorrect ordered array is returned... not sure why, cheers guys
this.bubbleSort = function (elements) {
//Loop through to the second to last index. By the time we get to the last index, its already //been compared with what’s in front of it
var hasHadChange;
for (var x = 0; x < elements.length - 1; x++) {
hasHadChange = false;
//Loop through to the second to last index.
for (var y = 0; y < elements.length - 1; y++) {
//Check the current item(x) in the array plus the item next to current item (x+1), if its larger
if (elements[y] > elements[y + 1]) {
//Acknowledge there has been a change
hasHadChange = true;
//Swap the items around
var temp = elements[y];
elements[y] = elements[y + 1];
elements[y + 1] = temp;
//This continues until the largest value has bubbled to the right
}
}
}
return elements;
}
You need to use separate variables for the inner and the outer loop. When you exit the inner loop, x will be equal to the length, so the outer loop will end also.
You should use separate variables in inner and outer loops. Using y in inner loop instead will give you the correct answer.
var bubbleSort = function (elements) {
//Loop through to the second to last index. By the time we get to the last index, its already //been compared with what’s in front of it
var hasHadChange;
for (var x = 0; x < elements.length - 1; x++) {
hasHadChange = false;
//Loop through to the second to last index.
for (y = 0; y < elements.length - 1; y++) {
//Check the current item(x) in the array plus the item next to current item (x+1), if its larger
if (elements[y] > elements[y + 1]) {
//Acknowledge there has been a change
hasHadChange = true;
//Swap the items around
var temp = elements[y];
elements[y] = elements[y + 1];
elements[y + 1] = temp;
//This continues until the largest value has bubbled to the right
}
}
}
return elements;
}
The reason behind this is variable hoisting.
When a variable is declared, it breaks into two parts. One part moves to top of it's scope, other stays at it's position. For Example,
function foo() {
if (false) {
var x = 1;
}
var y = 1;
}
Will look like :
function foo() {
var x = undefined; //Declaration of x is hoisted
var y = undefined; //Declaration of y is hoisted
if (false) {
x = 1; //Assignment still occurs where we intended
}
y = 1; //Assignment still occurs where we intended
}
This is what happened in the code. Using same variable in both loops makes them overwrite each other values. Hence the result.
From ECMAScript standard 5.1 :
A variable statement declares variables that are created as defined in 10.5. Variables are initialised to undefined when created. A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.
See this MDN doc for more details. Look for topic var hoisting.
Update
Using let which has block level scope, you can have variable x in both loops.
var bubbleSort = function (elements) {
//Loop through to the second to last index. By the time we get to the last index, its already //been compared with what’s in front of it
var hasHadChange;
for (var x = 0; x < elements.length - 1; x++) {
hasHadChange = false;
//Loop through to the second to last index.
for (let x = 0; x < elements.length - 1; x++) {
//Check the current item(x) in the array plus the item next to current item (x+1), if its larger
if (elements[x] > elements[x + 1]) {
//Acknowledge there has been a change
hasHadChange = true;
//Swap the items around
var temp = elements[x];
elements[x] = elements[x + 1];
elements[x + 1] = temp;
//This continues until the largest value has bubbled to the right
}
}
}
return elements;
}
You are using the same control variable x for both for-cycles, should use different like x and y for example.

Categories

Resources