JavaScript for loop issue affects guess count - javascript

so I'm trying to build a JavaScript hangman game and I'm having a problem with my checkMatch function. what I'm trying to achieve is for it to check against the hiddenChoice array and only run the code in the else if statement if this.id isn't in the array at all. currently if hiddenChoice = apple and this.id = l it will return 'guess again' 3 times before it returns that 'you found a letter' when it hits l, which affects my guess count. All of the console.logs are in there so I could figure out what was going on. thanks for the help.
function checkMatch(){
console.log(hiddenChoice)
for (let k = 0; k < hiddenChoice.length; k++){
if (this.id === hiddenChoice[k]){
console.log('you found a letter')
console.log(this.id)
greenColor = this.id
green(greenColor)
right++
console.log(right)
return
}
else if (this.id != hiddenChoice[k]) {
console.log('guess again')
console.log(guesses)
console.log(this.id)
redColor = this.id
red(redColor)
guesses--
}
}

The else shouldn't be inside the loop, because you cannot yet know whether any of the next characters would match. Only when the loop has finished you know that none of the letters matched.
So:
function checkMatch(){
console.log(hiddenChoice)
for (let k = 0; k < hiddenChoice.length; k++){
if (this.id === hiddenChoice[k]){
console.log('you found a letter')
console.log(this.id)
greenColor = this.id
green(greenColor)
right++
console.log(right)
return
}
}
console.log('guess again')
console.log(guesses)
console.log(this.id)
redColor = this.id
red(redColor)
guesses--
}
Some other remarks:
Your code uses several variables which are not declared, or at least global. This should be avoided. Declare temporary variables (like greenColor?) as local variables (e.g. with let).
End your statements with a semi-colon. JavaScript does this automatically when you don't, using some rules, but you don't want that to happen as there are some pitfalls.
It is not clear what this is. It would be better if id were passed as argument to the function.
Your code will not work as expected if a single guess has multiple matches. Try to think how you could manage that (as an exercise).

Related

if else preventing loop for passing through object

I am having trouble returning a statement when my RegExp finds no matches :
function ai(message) {
if (username.length < 3) {
username = message;
send_message("Oh, well hello " + username + ", My name is Donald J Trump and i'm a big boy");
} else {
for (i = 0; i <= botChat.length; i++) {
var re = new RegExp(botChat[i][0], "i");
if (re.test(message)) {
var length = botChat[i].length - 1;
var index = Math.ceil(length * Math.random());
var reply = botChat[i][index];
send_message(reply);
}
}
}
}
When I enter a phrase it can correctly match the first line in an array as per the for loop. The issue I'm having is when I try to add an else statement it ceases to loop through my array properly.
I have tried :
else if (re.test(message) === false) {
send_message("i can't be expected to know everything");
}
But it doesn't work, it prevents the loop from looping past botChat[0][0].
I've also tried :
if (send_message().val() == "") {
// ...
}
But every time my code no longer works. Is there a method of adding something to my array that says 'if you don't find a match above, choose this'?
Or just a way of working my code so that the if/else works?
attached is the codepen.
I checked your codepen and the lines 190 and 194 console.log(send_message().val()); seems to be breaking the loop because those lines are throwing an exception since send_message() returns undefined and undefined does not have the .val() method.
Your regular expressions are working fine. I recommend not creating new RegExp objects every iteration, instead, use the one defined in the array if (botChat[i][0].test(message)), the overhead will be less.

counter variable is holding 2 values on 2nd pass of the function

I'm making a typing game. When multiple players play the game it runs through the same set of functions again. I'm using the variable j as a counter to advance words when they are typed correctly. For some reason, on the second pass on each upkeystroke, it logs j = 1 & j = whatever the value of the previous players last word + 1 is. When each player plays, I want each set of words they are typing to be the same, so that it is fair. I can't figure out why this is happening or even how the variable has 2 values at the same time?!?!?
What gives?
Here's the code in question, but there's a bunch of callbacks that could be involved, although the only place this variable is called is inside this function.
//advances ship on correct typing
function runRace() {
timer();
var j = 1;
//BUG HERE !! Works fine on first iteration but on second
//iterations value jumps beteween 1 and whatever the next
//one is. It's like on every keystroke it reassigns var j
//back to 1, then back to the element number it was on
//last time
//!!! j has 2 values !!!it's keeping the value from the
//prior running of run race
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
}
If you need to see the rest of the code to figure this out, it's here. Eventhough it's not running right on jsfiddle for reason, it works other then the bug, locally https://jsfiddle.net/ujsr139r/1/
As i mentioned in my comment you're creating multiple listeners everytime runRace() is called.
You could try something like this instead (please note, this isn't the best way to do this, i'm just demoing. Global variables like j in this case aren't a clever idea.:
var j=1; // global because its outside of your function
$(function(){
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
});
//advances ship on correct typing
function runRace() {
j = 1;
timer();
}

Javascript: action when a string is not found in an array of objects (if statement)

var checkStore=function (book) {
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
}
}
}
}
How to add to my existing code "if the string is not found, then alert the user 'not found!'". Do I used [else] statement or begin a new one? It's not working correctly when I used else-if statement.
You might want to find the book first, then do your logic:
var checkStore = function(book) {
var found;
for (var i = 0; i < BookStore.length; i++) {
if (book == BookStore[i].title) {
found = BookStore[i];
break;
}
}
if (found) {
var reply = prompt ('Want to add to your cart?' + found.title);
if (reply === 'yes') {
Susan.cart.push(found);
}
} else {
alert('not found!');
}
}
var checkStore=function (book) {
if(BookStore.indexOf(book) < 0){
alert('hello, no book');
} else {
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
}
}
}
}
}
}
The other answers are totally valid. You can also just make return points which stop the execution of the function at logical endpoints.
var checkStore=function (book) {
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
return "book added to cart."
}else{
return "book found, but not added to cart."
}
}
}
return "bummer, book not found.";
}
alert(checkStore("book title"));
Fastest solution
This is the shortest way to take your exact existing code and add a few lines and solve this problem: How to add to my existing code "if the string is not found, then alert the user 'not found!'".
var checkStore=function (book) {
var bookWasFound=false; //Line added here
for (var i=0; i<BookStore.length;i++) {
if (book==BookStore[i].title) {
bookWasFound=true; //Line added here
var reply= prompt ('Want to add to your cart?'+ BookStore[i].title);
if (reply==='yes') {
Susan.cart.push(BookStore[i]);
}
}
}
if (!bookWasFound) { //Line added here
alert('Not found!'); //Line added here
} //Line added here
}
Explanation and alternatives
Why don't we use an else statement?
You ask, "Do I used [else] statement[...]?"
You do not. You can not! The reason is because the else part of an if-else statement is executed when the if part is not executed. So if we put that in our loop, every time we compare a book title to the string we're looking for, our else would be executed if the book title didn't match. We only want that to happen once.
It is somewhat difficult to think about this scenario without a concrete example, so I will show what happens if we use an if-else statement.
The code would look like this:
if (book==BookStore[i].title) {
...
} else {
alert('Not found!');
}
Imagine that our BookStore is populated with 3 books: "The C Programming language", "JavaScript: Pocket Reference", and "Everybody Poops".
If the checkStore function is passed in "JavaScript: Pocket Reference". Our loop will go like this:
As soon as we enter the loop, book would be equal to "JavaScript: Pocket Reference", and i = 0 so BookStore[i].title is the same as BookStore[0].title, and the 0th book's title is "The C Programming language".
So the line if(book==BookStore[i].title) { would compare "JavaScript: Poc..." to "The C Prog..." and evaluate to false.
Since the if evaulated to false, the else part gets executed. The else part is alert('Not found!').
Now, we have finished one iteration of our loop, and we compared the string passed in to the first book's title which didn't match, so we gave the alert. But we shouldn't have! If we just kept going, we would see that the second book's title matched, so we shouldn't have told the user that the book wasn't found. It is correct that the book wasn't found, yet, but we should have waited before we announced this to the user.
I may have went into great detail to explain my point, but I believe it is very important. This is one example of when it takes thinking like a programmer or thinking algorithmically to solve the problem.
You have to think about the problem and ask yourself, "How do we know if a book is in the list?". Well, a book is in the list if its title is the same as the title of any book in the list. Because we have to use the word any when we say "...any book in the list", we know that our algorithm must test every book in the list.
We can't tell the user that a book was found, or not found, until we have tested every book in the list, or in our BookStore. We can't tell the user that a book was found, or not found, until our loop has finished.
So what do we do?
You will see this situation over and over again during your programming experience. You need to iterate over a list, and determine if something is in the list.
You can't ever know until you've iterated over every single object in the list, but once you have, how do you know the answer?
The solution is to use what is commonly referred to in programming, a flag. A flag is a variable that has two possible states: true, or false, on or off, yes or no, 1 or 0. It doesn't matter what you call the states. At any time, the flag can only be in one of the two possible states.
We can use a flag by setting it to one state, usually false or 0, then looping through some list and if (...) we set it to true. Each iteration of the loop, if some condition is met, we set our flag to true, and at the end of the loop, we know that the condition was met at some point in the loop, if the flag is true.
The most common way to implement a flag, is to use a boolean variable. That is, a variable which can only be set to true or false. In Javascript, there isn't a boolean variable since any variable can be set to any value. So we just use a regular variable, but still only set it to true or false.
We do that like this:
var myFlag = false;
// OR
var myFlag = true;
So a simple usage of a flag could be to determine if there exists an even number in a given array.
Just like this:
var myArray = [1, 3, 5, 123, 125, 4, 89, 8, 10];
var hasEven = false;
for(var i = 0; i < myArray.length; i++) {
if( myArray[i] % 2 == 0 ) {
hasEven = true;
}
}
if(hasEven) {
alert('Found an even number!');
} else {
alert('Did not find an even number!');
}
Note that you can test the value of a boolean (true or false) variable with if(variableName), you don't have to include the == true like if(variableName == true).
Final implementation
Now we can use our flag to solve our original problem
var checkStore = function(book) {
var bookWasFound = false;
for (var i = 0; i < BookStore.length; i++) {
if (book == BookStore[i].title) {
// If we get in this loop, we must have found a match,
// so we should set our flag to true now
bookWasFound = true
var reply = prompt ('Want to add to your cart? ' + BookStore[i].title);
if (reply === 'yes') {
Susan.cart.push( BookStore[i] );
}
} // End of if statement for prompt
} // End of loop looking for book
if( !bookWasFound ) { // Same as if( bookWasFound == false )
alert('Not found!');
}
} // End of function
Using this flag allows us to keep the inside of the for loop very clean and organized, and handle the results outside of it.

JS: Using Length Property to Write If Statement

I'm very new to JS so go easy on me. I've got this array inside a variable, and am trying to find a better way to write that if statement. So if the names inside that variable grow, I won't need to change the if statement as it won't be hardcoded.
var names = ["beth", "barry", "debbie", "peter"]
if (names[0] && names [1] && names [2] && names [3] {
Do something...
}
Something tells me I need to be using the .length property but I can't work out how to properly use it within that statement. Something along the lines of:
if (names[i] * names.length) {
Do something...
}
I know that's wrong. I think need to be finding the index of each and looping through it makign sure it the loop doesn't exceed the amount of values in the array.
Any help is appreciated. Thanks in advance!
Update: Some users have alerted me that my question might not be as clear. I've setup a CodePen here (http://codepen.io/realph/pen/KjCLd?editors=101) that might explain what I'm trying to achieve.
P.S. How do I stop my from repeating 3 times?
You can use every to test whether every element satisfies some condition:
if (names.every(function (name) { return name })) {
// Do Something
}
every will automatically stop testing when the first non-true element is found, which is potentially a large optimization depending on the size of your array.
Traditionally, you would simply iterate over the array and test each element. You can do so with forEach or a simple for loop. You can perform the same early-termination when you find a non-true element by returning false from the forEach callback.
var allTrue = true;
names.forEach(function (name) {
return allTrue = allTrue && name;
});
if (allTrue) {
// Do something...
}
Please give a english description of what you are trying to accomplish. The below answer assumes you simply want to iterate a list of names and do some processing with each.
You want to use a for loop.
var names = ["beth", "barry", "debbie", "peter"]
for (var i=0; i<names.length; i++) {
// access names[i]
}
The best cross-browser solution is to use a traditional for loop.
var names = ["beth", "barry", "debbie", "peter"],
isValid = true,
i;
for (i = 0; i < names.length; i++) {
isValid = isValid && names[i];
}
if (isValid) {
// do something
}
You can try this;
var checkCondition = true;
for(var i = 0; i<names.length; i++){
if(names[i] !== something) {
checkCondition = false;
break;
}
}
if(checkCondition){
//Do what ever you like if the condition holds
}else{
// Do whatever you like if the condition does NOT holds
}
If i understand right you need something like this
var names = ["beth", "barry", "debbie", "peter"];
var notUndefinedNames = names.filter(function(el){return el !== undefined;});
// if all
if (names.length === notUndefinedNames.length) console.log("You're all here. Great! Sit down and let's begin the class.");
// if one or less
else if (notUndefinedNames.length <= 1) console.log("I can't teach just one person. Class is cancelled.");
else console.log("Welcome " + notUndefinedNames.join(', '));

How to restructure .forEach so I can break out of it

I have had a lot of trouble with JavaScript and 'break' statements coming from a Ruby background.
Here is my function:
function isItTheNumber(numberToGuess){
if(previousGuess === null){
previousGuess = [playersGuess];
}
previousGuess.forEach(function(guess){
if(playersGuess === guess && previousGuess > 1){
textStatus.style.display = 'block';
textStatus.innerHTML = 'You already picked that number';
} else if(parseInt(playersGuess) === parseInt(numberToGuess)){
textStatus.style.display ='block';
textStatus.innerHTML = 'You Are CORRECT!';
} else {
previousGuess.push(playersGuess);
textStatus.style.display='block';
textStatus.innerHTML = 'You are ' + hotOrCold();
document.getElementById('guess-count').innerHTML = playerGuessCount++;
}
});
}
In my .forEach loop I would like to have a 'break' statement in my first if statement. I would like the loop to stop if it ever executes this block.
I realize I can't use a break statement in this forEach function after reading a few posts about it. I attempted the suggestion here using "every" but when I was using this wrapped in a function I was not able to return a true or false value.
I would like to avoid having to use any type of 'break' or hack for the break but will use it if it is the only way. If anyone has any suggestions on how I can re-work my logic or have any suggestions I would appreciate it. I'll list my logic in pseudo code below.
1) Check if the previousGuess array is null or populated. If it is null, set it to an array with the first value.
2) Iterate over the previousGuess array.
3) If: see if the user input (playerGuess) is in the previousGuess array. The previous guess
array must be larger than 1.
4) Else If: If the users guess is the same as the a the value, tell them they are correct.
5) Else: if the users guess isn't the same as the value, tell them and add 1 to the playerGuessCount.
The problem with my current logic is that the playerGuessCount is being invoked too many times. If the array is being iterated over and finds and the first if statement is true, it will still iterate over the rest of the array, adding 1 to the playerGuessCount even when they only submit 1 guess. The .forEach is strictly there to check if their guess is a repeat.
Here is my attempt with 'every' http://repl.it/P74
The .forEach method you are using is a wrapper implemented by extending a function prototype.
You can break out of a conventional loop:
for (var key in objs){
var obj=objs[key];
//do your business here
break; //this line exists the loop
}
However, if a callback function is used, you have to terminate the function call itself by placing a "skip" variable.
previousGuess.forEach(function(guess){
if (this.skip_the_foreach_loop) return;
if (need_to_exit) this.skip_the_foreach_loop=true;
});
Not the most efficient way but saves you a few CPU cycles.
Would this work for what you're trying to do?
function isItTheNumber(numberToGuess){
if(previousGuess === null){
previousGuess = [playersGuess];
}
// Using Array.prototype.every so a falsey return breaks
previousGuess.every(function(guess){
if(playersGuess === guess && previousGuess > 1){
textStatus.style.display = 'block';
textStatus.innerHTML = 'You already picked that number';
return; // returns undefined which is falsey and breaks loop.
} else if(parseInt(playersGuess) === parseInt(numberToGuess)){
textStatus.style.display ='block';
textStatus.innerHTML = 'You Are CORRECT!';
} else {
previousGuess.push(playersGuess);
textStatus.style.display='block';
textStatus.innerHTML = 'You are ' + hotOrCold();
document.getElementById('guess-count').innerHTML = playerGuessCount++;
}
return true; // Meaning go to next iteration
});
}

Categories

Resources