Loop for when i exclude one element - javascript

i have this variable:
qtdPerguntasGerarQuiz = 2
and i have this array:
idQuestoesUnidadeLivro = [3,4,50]
for (let k = 0; k < qtdPerguntasGerarQuiz; k++) {
pergunta = await this.getPerguntaAleatoria(idQuestoesUnidadeLivro) // here i get one object
if (pergunta.rows[0].type_answer === 'Multipla Escolha') { // is entering in this if
optionCorreta = await this.getOptionCorreta(pergunta.rows[0].id)
if(!optionCorreta.rows[0]){ //also entering here
for(let i_find = 0; i_find < idQuestoesUnidadeLivro.length; i_find++){
if(idQuestoesUnidadeLivro[i_find] === pergunta.rows[0].id){ //is entering here and excluding the idQuestoesUnidadeLivro[i_find] that is equal to pergunta.rows[0].id
idQuestoesUnidadeLivro.splice(i_find,1)
qtdPerguntasGerarQuiz--;
break; //don't continue the for of i_find
}
}
return
}
// Continue the process if don't enter in the condition if(!optionCorreta.rows[0])
optionsPergunta.push(optionCorreta.rows[0])
...
When i'm excluding one item of my array idQuestoesUnidadeLivro.splice(i_find,1)) i need to stop the for (because i find the element that i need to exclude), if the condition is true, this element don't have appear again in the first for(), so i splice() and i decrement 1 to avoid loop.
If the element is excluded, i need to return to the start of the first for().
My problem is when my condition enter in the if(idQuestoesUnidadeLivro[i_find] === pergunta.rows[0].id) the for entering in eternal loop and i can't find why.

Try to remove return command after if.
It could finish your function by return.
And update break to the below.
OUTER_LOOP: for (let k = 0; k < qtdPerguntasGerarQuiz; k++) {
...
qtdPerguntasGerarQuiz--;
break OUTER_LOOP; //don't continue the for of i_find

In your code, when the condition !optionCorreta.rows[0] meets, then the function ends, no matter what any other condition check or the splice action happens.
So let's try to refactor your code by removing the return statement:
for( ) {
if( !optionCorreta.rows[0] ) {
....
for( ) {
if( ) {
splice()
break;
}
} else {
optionsPergunta.push(optionCorreta.rows[0]);
}
}
In this way when the break statement reaches, it exits the inner loop. Now since the other lines are inside the else, then it gets back to start of the first loop.
In the case of infinite loop, did you step through your code in the node debugger? There may be problems with getPerguntaAleatoria(idQuestoesUnidadeLivro) or getOptionCorreta(pergunta.rows[0].id) methods.

try using a flag for control push line
idQuestoesUnidadeLivro = [3,4,50]
let mustPushOptionCorreta = true;//***
let addedPerguntasGerarQuiz = 0;
for (let k = idQuestoesUnidadeLivro.length - 1; k >= 0 && addedPerguntasGerarQuiz < qtdPerguntasGerarQuiz; k--) {
pergunta = await this.getPerguntaAleatoria(idQuestoesUnidadeLivro) // here i get one object
if (pergunta.rows[0].type_answer === 'Multipla Escolha') { // is entering in this if
mustPushOptionCorreta = true;//***
optionCorreta = await this.getOptionCorreta(pergunta.rows[0].id)
if(!optionCorreta.rows[0]){ //also entering here
for(let i_find = 0; i_find < idQuestoesUnidadeLivro.length; i_find++){
if(idQuestoesUnidadeLivro[i_find] === pergunta.rows[0].id){ //is entering here and excluding the idQuestoesUnidadeLivro[i_find] that is equal to pergunta.rows[0].id
idQuestoesUnidadeLivro.splice(i_find,1)
qtdPerguntasGerarQuiz--;//I guess you should remove this line
mustPushOptionCorreta = false
break; //don't continue the for of i_find
}
}
}
// Continue the process if don't enter in the condition if(!optionCorreta.rows[0])
if(mustPushOptionCorreta) {//***
optionsPergunta.push(optionCorreta.rows[0])
addedPerguntasGerarQuiz++
If I guess right, you want create a quiz with qtdPerguntasGerarQuiz questions in it. Why when you select an inappropriate question, reduce qtdPerguntasGerarQuiz?

Related

Leetcode Two sum problem question about why my code doesn't work

Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
var twoSum = function(nums, target) {
let comp = {};
for(let i =0; i<nums.length; i++){
let match = target - nums[i]
my question is why doesn't my code work if remove comp[match]>=0 and use comp[match] instead?
if(comp[match]>=0){
return [comp[match], i]
console.log(comp)
}
else{
comp[nums[i]]= i
}
console.log(comp)
}
};
Snippet:
var twoSum = function(nums, target) {
let comp = {};
for (let i = 0; i < nums.length; i++) {
let match = target - nums[i]
if (comp[match]) {
return [comp[match], i]
console.log(comp)
} else {
comp[nums[i]] = i
}
console.log(comp)
}
};
twoSum([2, 7, 11, 15], 9)
The idea behind comp is to store the indexes of the values previously seen while you're looping through your array of numbers. This means that it is possible for a key in your object to point to the index 0.
In JavaScript, 0 is considered falsy, so when put into a if statement, it will skip the if block as it is considered false, and instead, execute the else block.
if(0) {
console.log("truthy"); // doesn't execute
} else {
console.log("falsy");
}
So, if you were to use if(comp[match]) and comp[match] was to give you the index value of 0, your else block would trigger, when instead, you actually need your if block to trigger (as you have previously seen a number which you can now add with the current number). That's why the following works as expected:
if(comp[match] >= 0)
In this scenario, if comp[match] returns back the index value of 0, the code in your if-block will be triggered as needed. It is possible for comp[match] to return undefined though. In this case, your else block will trigger so your code will work fine (as undefined >= 0 is false). However, if you want to make your condition more readable you can instead use:
if(conp[match] !== undefined)

Nested IF-FOR-IF loops

I'm stuck in nested loops.
this.isRyt is a variable where a retrived string from JSON is stored.
i is a user input variable of type string.
this.storeArray[] is an array where every input of i from user will be stored only if it matches the string stored in this.isRyt variable.So basically I want to compare the strings stored in this.storeArray[] by using index k to the string stored in this.isRryt(as multiple i inputs from user will stored at different index locations in this.storeArray[]),and if the string is not matched then there is variable counter which will get incremented.incCounter is nothing but a simple counter variable initialized with value 0.
My try: I tried using the below loop , but this.counter++ get incremented multiple times in a single time(multiple iterations of k) as it is inside the for loop. I want to make it increment only a single time but the for condition should not be omitted.
filterAnswer(i:any) //Comparing Answer submitted by user with JSON answer
{
this.isRyt = this.questArrayNew1[0].isRight;
if(this.isRyt == i )
{
for(let k = 0 ; k < this.questArray.length ; k++)
{
if(this.storeArray[k] == i)
{
console.log(k);
}
else
{
this.counter++; //WANT TO INCREMENT ONLY ONE TIME IF IT DOESNT SATISFY THE CONDITION FOR WHOLE K=0,1,2,3.. variable
}
}
this.storeArray[this.incCounter] = i ;
console.log(this.storeArray);
this.incCounter++;
}
else
{
return 0;
}
}
If I am understanding you correctly, this.counter only needs to be incremented once. You could try something like this:
filterAnswer(i:any) //Comparing Answer submitted by user with JSON answer
{
var notCounted = true; //condition for this.counter++ code block to be executed
this.isRyt = this.questArrayNew1[0].isRight;
if(this.isRyt == i )
{
for(let k = 0 ; k < this.questArray.length ; k++)
{
if(this.storeArray[k] == i)
{
console.log(k);
}
else
{
while(notCounted)
{ //executes while bool is true
this.counter++;
notCounted = false; //incremented so now no longer needed
}
}
}
this.storeArray[this.incCounter] = i ;
console.log(this.storeArray);
this.incCounter++;
}
else
{
return 0;
}
}

Using an array of numbers to add up to a specific number

I've been trying to find out an efficient method of finding multiple numbers from an array that add up to a given number. In this instance I'm trying to find 3 numbers that total a target number.
I've got a basic working example below but unfortunately the recursive loop fails, it looks like there's an issue with it constantly looping. Ideally it would find the first possible answer and return it, but when it can't find an answer it gets stuck in the loop and breaks the browser.
Warning: the below code will break due to a memory leak:
let array = [5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2,5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2,5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2];
function findSums(arr, target, count) {
var result = [];
function recurse(start, leftOver, selection) {
if (leftOver < 0) return; // failure
if (leftOver === 0 && selection.length == count) {
result.push(selection); // add solution
return;
}
for (var i = start; i < arr.length; i++) {
recurse(i, leftOver-arr[i], selection.concat(arr[i]));
}
}
recurse(0, target, []);
return result;
}
// Demo
$('#input').on('blur', function(e){
let value = parseInt(e.target.value);
let result = findSums(array, value, 3);
if(result.length > 0){
$('#output').text(result[0]);
} else {
$('#output').text('Nothing found');
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Input Number Below</h1>
<input id="input" type="number" />
<code id="output" ></code>
Well, it didn't break, when I tested, but still here are some tips:
You should set additional limitation to the count. You are making to much extra calls. When your function deals with really big sum, small numbers and small count it will call itself again until it reaches or overflows the desired sum, and only after that it will check current count. So you should add
if (selection.length > count) return;
Also. As I see there are many duplicates in your array, so I assume, that usage of the same number is allowed, but only if it is taken from another index. In your loop you are calling next recurse with the same start index. I think, you need
for (var i = start; i < arr.length; i++) {
recurse(i + 1, leftOver-arr[i], selection.concat(arr[i]));
}
And finally. This will not influence the recursive part of an algorithm, but maybe you'd like to filter out same results, or filter out your array to remove all duplicates.
Hope this helps.
Edit: sorry, missed the part about first possible solution. Here is the way to achieve this:
function recurse(start, leftOver, selection) {
if (leftOver < 0) return false; // failure
if (selection.length > count) return false;
if (leftOver === 0 && selection.length == count) {
result.push(selection); // add solution
return true;
}
for (var i = start; i < arr.length; i++) {
var res = recurse(i + 1, leftOver-arr[i], selection.concat(arr[i]));
if (res) return true;
}
}

Recursive function that generates the permutations of a string

//====================================================
function getPermutations(str){
//Enclosed data to be used by the internal recursive function permutate():
var permutations = [], //generated permutations stored here
nextWord = [], //next word builds up in here
chars = [] //collection for each recursion level
;
//---------------------
//split words or numbers into an array of characters
if (typeof str === 'string') chars = str.split('');
else if (typeof str === 'number') {
str = str + ""; //convert number to string
chars = str.split('');//convert string into char array
}
//============TWO Declaratives========
permutate(chars);
return permutations;
//===========UNDER THE HOOD===========
function permutate(chars){ //recursive: generates the permutations
if(chars.length === 0)permutations.push(nextWord.join(''));
for (var i=0; i < chars.length; i++){
chars.push(chars.shift()); //rotate the characters
nextWord.push(chars[0]); //use the first char in the array
permutate(chars.slice(1)); //Recurse: array-less-one-char
nextWord.pop(); //clear for nextWord (multiple pops)
}
}
//--------------------------------
}//==============END of getPermutations(str)=============
How is nextWord.pop() getting called multiple times?
Won't permutate(chars.slice(1)); not let nextWord.pop() execute since it will take you back to the top of the permutate function?
Also, when chars becomes empty from calling slice on it permutate(chars.slice(1)); who is populating chars once again? Is chars being populated by nextWord.pop(); since pop is returning the value to the permutate function?
Stepping through this code in chrome debugger it wasnt clear.
Recursive call permutate is inside a loop and each time it is executed it is put on the call stack. The nextWord.pop is called multiple times to finish executing each of the recursive calls on the stack. You can visualize the recursion with this tool http://visualgo.net/recursion.html. If you have a tool such as Webstorm, you can run it in the debugger to see that there are three permutate() calls in the stack when the first time nextWord.pop is called.
It would execute after premutate() returns. But I guess that's obvious from looking at the code. I think your question is how can premutate ever reutrn? The answer to that is to look at the for loop.
Since we're calling premutate() with one fewer character every time. It makes sense that at some point one of our calls to premutate() will be called with an empty array:
premutate([]); // happens when chars.slice(1) gives us an empty array
Now, let's see what happens when that happens:
function permutate(chars){
// The following gets executed:
if(chars.length === 0)permutations.push(nextWord.join(''));
// This for loop is skipped because 0 < 0 is false
for (var i=0; i < chars.length; i++){/*...*/}
// we return because we skipped the for loop
}
Now that the base-case have returned, all the other calls to premutate() also returns:
function permutate(chars){
if(chars.length === 0)permutations.push(nextWord.join(''));
for (var i=0; i < chars.length; i++){
chars.push(chars.shift());
nextWord.push(chars[0]);
permutate(chars.slice(1)); // This have returned..
nextWord.pop(); // so execute this line
}
// and return so that other calls to us can also execute pop()
}
chars.slice(1) is the character array chars starting at position 1, i.e. the second character, so calling permutate(chars.slice(1)); recurses with 1 fewer characters.
Eventually, chars.slice(1) will return zero characters, at which point permutations.push(nextWord.join('')); is executed and for (var i=0; i < chars.length; i++) will not execute the loop block inside because its terminating condition i < chars.length will already be false when the initial value of i is 0 and chars.length is also zero.
So the terminating condition of this recursive function is when it runs out of characters in the current word.
When the the permutate function finally returns, nextWord.pop() is called once for each time permutate was called.
Calling permutate will not reset you at the top of permutate. (as was mentioned by joe) It will simply wait for this child call to complete and then continue executing the method.
I think your recursive permutate could be simplified:
// suppose input = "abc"
permutate(chars) {
if(chars.length === 2) return [chars, chars[0] + chars[1]};
var arr = permutate(chars.slice(1)) // returns ["bc", "cb"]
var out = []
for (var i=0; i < arr.length; i++) {
for (var j = 0; j < chars.length - 1; ++j) {
out.push(arr[i].split(0,j) + chars[0] + arr[i].split(j)) // "a" gets inserted at every possible position in each string
}
}
// result here is ["abc", "bac", "bca", "acb", "cab", "cba"]
return out
}
function permutate(left, used, result) {
// If there are no more characters left to permute
if (0 == left.length) {
result.push(used);
}
// Iterate over all characters in the 'left' string
for (var i = 0; i < left.length; ++i) {
// Read the character we are going to work with in this iteration
var iObject = left[i];
// Create a new_left string that contains all the characters
// of the 'left' string without the one we are going to use now
var new_left = '';
for (j = 0; j < left.length; ++j) {
if (j != so.i) new_left += so.left[j];
}
// Create a new_used string that has all the characters of 'used'
// plus the one we are going to use now
var new_used = so.used + iObject;
// Call permute with new_left and new_used strings
permutate(new_left, new_used, result);
}
}
To run the function on some string you need to call it like this:
permutate('abcd', '', []);
For those who doesn't get this because of the recursion involved I found a page that illustrates the algorithm flow using interactive animations.
http://learntocode.guru/code/generate-permutations-recursively-string
Just click play and observe the variables change while the permutation function keeps calling itself. Also observe when a new permutation has been found and when it's added to the resulting permutations array.

How can I skip a specific Index in an array in a for loop javascript

Suppose I had a function that is pulling in values from somewhere and storing those values into an array.
function getSport(ply) {
some code here... //function gets values that I need for array later
}
var sports1 = getSport(playerChoice);
var sports2 = getSport(playerChoice);
var sports3 = getSport(playerChoice);
var sports4 = getSport(playerChoice);
var sportsArry = [sports1, sports2, sports3, sports4];
Now I would like to use a for loop to loop the elements, the problem, however, is the first index (index 0) will always be true. I want to skip index 0. How do I do that? Further I want to replace index 0 with something else. Let me show you
for (var i = 0; i<sportsArry.length; i++){
if ( (sports1 == sportsArry[i]) ) {
sports1 = null; //I figured I should null it first?
sports1 = replaceValueFunc(playerChoice2);
}
}
Well you can see the problem I would have. Index 0 is true.
Let me show you what would work, although it requires alot of or operators.
if ( (sports1 == sportsArry[1]) || (sports1 == sportsArry[2]) || (sports1 == sportsArry[3] ) {
...
}
^^ That is one way to skip index 0, what would be another better looking way?
I want to skip index 0. How do I do that? Further I want to replace
index 0 with something else.
Just start the loop from 1 instead of 0
sportsArr[0] = "Something else"; // set the first element to something else
for(var i = 1; i < sportsArr.length; i++){
// do something
}

Categories