My wife Nikki is currently attending school for web development and the questions she brings to me are always very interesting. At first blush her questions are understandably beginner questions that I feel like I should have an easy time answering. But then I find myself struggling to properly explain why certain things are the way the are, or when they should be used. It's been so long since I was a beginner that it's sometimes hard for me to put myself in her shoes and actually see where the disconnect is.

One of the things she has been struggling with is when to create a variable. The question caught me off guard because to me the answer is "when you need one." She has asked me questions like "How did you know to create a variable there?" and I find myself stuck trying to understand what she means. For example, she was tasked with writing a hangman game. We had to create an event handler for when the user presses a key and then we needed to figure out if the letter was a correct or incorrect guess. To do that we needed to loop over each character in the word and compare that character to the key that was pressed. Keep in mind we're using only code she has learned in school so far, so just simple for loops, no fancy regular expressions or anything.

var currentWord = "Tabby";
var correctGuesses = [];
var wrongGuesses = [];

document.onkeydown = function(event) {
  for (var index = 0; index < currentWord.length; index++) {
    var letter = currentWord[index];
    if (letter.toLowerCase() === event.key) {
      correctGuesses.push(letter);
    }
  }
}

We had gotten as far as the code above where we detect if the letter is a match for the user's guess and then we add the letter to the correctGuesses array. Then it was time to figure out how to track incorrect guesses. She kept wanting to put an else statement on that if and log the incorrect letter there. Like this:

for (var index = 0; index < currentWord.length; index++) {
  var letter = currentWord[index];
  if (letter.toLowerCase() === event.key) {
    correctGuesses.push(letter);
  } else {
    wrongGuesses.push(event.key);
  }
}

Can you spot the problem there? We are looping over every letter of the word. In this example our word is "Tabby" so we are going to loop over all five letters and on each letter we are going to compare the letter from the word to our user's guess. If it matches then we correctly add that match to the correctGuesses array, if we follow what she wanted to do and add that else statement then we are going to also add failed matches to the wrongGuesses array. Let's go over each iteration of our for loop and examine what happens in this scenario. We'll pretend our user pressed y for their guess.

  1. letter is "T".
    If "T" is equal to "y" then add it to correctGuesses.
    If "T" is not equal to "y" then add it to our wrongGuesses.
    In this case the second one is true so we add "y" to our wrongGuesses array.
  2. Now we are on the second iteration of the for loop and letter is "a".
    If "a" is not equal to "y" then we add "y" to wrongGuesses again.
    Our "wrongGuesses" array now contains two "y" entries.

Do you see the problem? We've only checked the first two letters of the word for a match so far and we recorded "y" as a wrong guess in both cases just because they didn't match the first couple letters of the word. The user guessed "y" and we know there is a "y" at the end of the word "Tabby" so when the loop gets to the final letter of the word it will be true that "y" equals "y" and we will add "y" to the correctGuesses array.

The issue is that we need to finish looping through the entire word and only add the user's guess to wrongGuesses if there was never a correct match in any of the iterations of our loop. So how do we solve this? The easiest way seems to me to use a variable to keep track of this. The first thing that comes to mind is a boolean variable that we can set to show we got a match. Then we can use that variable after the loop to see if there was a match or not.

var matched = false;
for (var index = 0; index < currentWord.length; index++) {
  var letter = currentWord[index];
  if (letter.toLowerCase() === event.key) {
    correctGuesses.push(letter);
    matched = true;
  }
}
if (matched === false) {
  wrongGuesses.push(event.key);
}

So now we have a matched variable that is set to false before our for loop. Remember, if the user made a correct guess it's still not going to match most of the letters in the word. In other words, their guess is only going to match some letters of the word, not all 5. So we can't go setting matched to false whenever there isn't a match because what if their correct guess was in the middle of the word? Say they guessed "a". The first iteration of the loop would set matched to false. The second iteration of the loop would set matched to true. Then the third iteration of the loop would set matched back to false. Almost the same problem as having the else statement inside the loop recording false wrong guesses. When the loop was done matched would be false even though we really did have a match at some point in the loop.

So that's why we set matched to false outside the loop. We need a variable that stays in one state and only changes if a specific condition is met. In our case that condition is whether or not any of the letters in the word matched our user's guess. So now let's pretend again that our user guessed "a". matched is false before the loop even begins. On the first iteration of the loop "a" does not equal "T" so it does nothing. On the second iteration of the loop "a" does equal "a" so we change matched to true. On the third iteration of the loop "a" won't match and just like the first iteration nothing will happen. The loop will finish and after the loop matched will have been flipped to true and stayed that way. If the user had guessed "z" for example then nothing would have happened to our matched variable on any iteration of our loop and it would have stayed false the whole way through. Now the loop is done and we have a variable that is either true if one of the letters matched the user's guess, or false if none of the letters in the word matched the user's guess.

I like to call variables like this "meta variables" because they are set at a higher level outside of a particular operation and then modified somewhere inside that operation so that when the operation is over you now have some "metadata" about what happened during that operation. Honestly, this is all stuff I've never thought that hard about and have never had to put into words like this. I believe that is why I was finding myself having a hard time explaining just exactly why I was doing things the way I was doing them. Nikki told me the matched variable confused her a lot and claimed she would never have been able to figure that out on her own. I'm not so certain that last part is true but I am starting to see how something like this can seem more complicated than it is.

To Nikki, declaring that matched variable and setting it to false just looked useless. We made a variable and set it to false. She just couldn't understand its purpose. She just kept saying "Why can't we just check if the guess matched and if it didn't then mark it wrong?" I think the confusion stemmed from the fact that we were checking if the guess matched five times, once for each letter. We can't mark them wrong until we know their guess didn't match any of those five letters. We can just add the letter to correctGuesses the second we know it matches a letter, but when it doesn't match one letter it doesn't mean it won't match another letter later in the word. So that's why we were able to add the correct guesses right away, but needed some way to see that the user's guess was wrong all five times before we could add the incorrect guess to wrongGuesses.


So when do we need variables? Well... the answer is still "when you need one" but how you decide that is up to you. You have to think about the problem you're trying to solve conceptually. In this example "How do we know that the user's guess was wrong?" Or more accurately, "How do we know that the user's guess was wrong for each letter in the word?" We know that we don't want to record a guess as wrong if they got it right. So we just loop over the letters, and set a variable to true if they got one or more letters correct. If their guess matches 2 or more letters in the word then we will be setting matched to true redundantly, but that's okay as long as matched is true when the loop is over so that our if statement after the loop can decide if there were any matches or not, and if not then it can record the wrong guess.

This problem is a perfect example of a problem that seems easy at first but gets seemingly complicated as you try to think about it like a computer. Your brain operates very similarly but it happens so fast you don't really realize it. If you play Hangman on paper your brain would do exactly what our code above was doing; you would look over each letter of the word and then fill in the letters that matched your friend's guess. If none matched then you would mark down the guess as incorrect. In your head you are doing a loop, going through each letter one by one and only determining the guess is wrong after you've gone through all the letters and never got a match.

You created a little boolean variable in your brain that said "nope, no matches so far" as you went through each letter. Then if you find a match a long the way you flip that variable in your head from "none so far" to "yep! found one" and you fill in the correct guess on the paper. But you don't stop there. You keep looping through the rest of the word's letters to see if you find any more matches since there could be more of the same letter in the word and if you find any then you fill those in also as you find them. Only when you're all done looking through each letter of your word do you then refer to your memory (i.e. the variable in your head) to see if it still says "nope, no matches" or if you changed it to "Yep!". If you use your brain's memory to determine that there were no matches while looking over your word's letters, then you can write down that the guess was wrong. If somehow you "forgot" the result of each match as you went over each letter, then you might have recorded the correct guesses as you got to each one, but by the end of the word you wouldn't be able to "remember" if you had any correct guesses and would be unable to determine if their guess was wrong. Thank goodness you do have the ability to store things in your brain's memory ;)

It's the exact same with a computer. Variables are memory space. In fact, they literally point to memory addresses on your computer that are stored in RAM. Your programming variable is actually pointing to a space in RAM that contains some ones and zeros representing some data. In our case it was a boolean, so the memory space in RAM was literally just a one or a zero. We set it to zero before our loop, and then if they ever get a correct guess then we go ahead and flip that bit to a one in RAM. Then after the loop we can go back and read that memory to say "oh yeah, it's still zero" and know that they never had a correct guess.

I hope these analogies and walkthroughs help you if you have a similar trouble understanding what you're doing when you create a variable.