FAQ: Loops - The While Loop

DAZED AND CONFUSED
Im not entirely sure where I stand in regards to this excersise, everything is different from the example gaved, just introducing the very concept of the while loop, i.e:

let counterTwo = 1;
while (counterTwo < 4) {
console.log(counterTwo);
counterTwo++;
}

and what we’re getting now is:

const cards = [‘diamond’, ‘spade’, ‘heart’, ‘club’];
let currentCard = “”
// Write your code below
while (currentCard !== “spade”){
currentCard = cards[Math.floor(Math.random() * 4)];
console.log(currentCard)
}

First lets begin with why is the iteration variable not the one that has the array and how does the execution of this loop play into that.

Second why with the exercise we’re all of a sudden changed the previously explained rules and in the code block of the while statement there is no incremental or decremental operators in what should be the iteration statement, there is a rule where we’re assigning the iteration variable on, not a number between 0 to 3 but an index between 0 and 3 of the array of the “cons” variable

I mean I guess what I would like explained is how does all these differences add up to output such diverse prints to the console…in one we have a set of 4 cards all in different order, then we run the code and get new cards (even repeated ones), but this time is 3 strings/indexes (still makes sense), but I’ve gotten even 6 “cards” or 6 different outputs…then why is the “Math.floor(Math.random() * 4)” of any use if the outputs go more than 4 I’m really deeply confused. Codecademy should explain this excersise with more depth and precission.

The basic structure of a while loop involves a condition and the body of the loop.

The condition can be any expression which evaluates to true/false. In the example mentioned by you, the condition is the expression (counterTwo < 4) which will evaluate to a boolean (true/false) value. In the exercise the condition is the expression (currentCard !== "spade") which also evaluates to a boolean value. In the example, counterTwo has been initialized before the loop with a value of 1. In the exercise, currentCard has been initialized as an empty string. With these initializations, we are guaranteed that we won’t skip the while loop. If counterTwo had been initialized as 7 or currentCard had been initialized as "spade", we would skip the while loops (because the loop condition would evaluate to false). Our conditions can be pretty simple or much more complicated expressions. As long as they evaluate to a boolean value (truthy or falsy are also allowed), they can be used as the condition of a while loop. So, there is a lot of flexibility in how we choose our conditions. We do have to make sure that any variables in the conditions should have been assigned some values prior to the while loop. If we use a variable which hasn’t been declared somewhere earlier in our code, then we are likely to see a ReferenceError about using a non-existent variable.

Once we enter a while loop and the body of the loop is executed, then it is essential that within the loop body, there is some statement (increment/decrement OR a break statement OR some other statement which updates some variable(s) which causes the loop condition to become false) so that we can eventually exit the loop. Otherwise, we will end up in an endless cycle (infinite loop). In the example, this is accomplished by the increment statement    counterTwo++;   . If we omit this statement, then we would be stuck in an infinite loop because the loop condition (counterTwo < 4) would never become false.
In the exercise, we are accomplishing this via the statement,

currentCard = cards[Math.floor(Math.random() * 4)];

We aren’t using an increment/decrement or break statement. Instead we are updating the value assigned to the currentCard variable. This variable is also present in the loop condition (currentCard !== "spade"), so we expect that at some point the string "spade" will be assigned to currentCard and consequently, the loop condition will become false allowing us to exit the loop.

The expression

Math.floor(Math.random() * 4)

will randomly select a whole number 0 or 1 or 2 or 3.

The cards array has four string elements. The first element is at index 0, the second at 1, the third at 2 and the last element is at index 3 i.e.

// If the array is 
const cards = ['diamond', 'spade', 'heart', 'club'];

// then we can access the elements by using bracket notation
// using the syntax:     arrayName[index] 

console.log(cards[0]); // "diamond"
console.log(cards[1]); // "spade"
console.log(cards[2]); // "heart"
console.log(cards[3]); // "club"

(Math.random() - JavaScript | MDN) returns a random floating-point number in the range [0, 1) i.e. in the interval (0random_number < 1).

(Math.random() * 4) scales the interval to [0, 4) i.e. (0random_number < 4).

Math.floor(Math.random() * 4) converts the floating-point (decimal) number to an integer so that one of the integers 0, 1, 2, 3 is randomly chosen. (See documentation: Math.floor() - JavaScript | MDN for examples of how floating-point numbers are converted to integers by rounding down to closest integer)

Since there are only four elements in the cards array (with their index ranging from 0 to 3), so the 4 in the above expression is meant to ensure that we don’t go out of bounds of the array (If the cards array had 6 elements, then we would have used 6 as our scaling factor).

currentCard = cards[Math.floor(Math.random() * 4)];

In each iteration of the while loop, a number/index will be randomly selected from 0, 1, 2, 3.
cards[random_index_here] will select one of the strings from the cards array.
Since this is a random process, so we don’t know when we will get a specific index. The string "spade" is at index 1 in the cards array. It could take one iteration or eight iterations or twenty iterations or …
We don’t exactly know how many iterations it will take. Hence, the different output sequences you see every time you re-run the code. But, with just 4 elements in the array, we can expect that sooner or later, we will indeed randomly select 1 as the index. And therefore "spade" will be assigned to the currentCard variable causing the loop condition to eventually become false allowing us to exit the loop.

Thank you for this explanation. This has helped me to understand this activity.

The following code produces a bad diagnostic Did you check if currentCard is equal to 'spade'? when what it is actually objecting to is the use of cards.length in the random calculation. This diagnostic is not only irrelevant, it should not be considered an error at all.

const cards = ['diamond', 'spade', 'heart', 'club']; // Write your code below let currentCard; while (currentCard !== 'spade') { currentCard = cards[Math.floor(Math.random() * cards.length)]; console.log(currentCard); }

While there is no error in the above example, the lesson checker may be looking specifically for a code pattern that reads, if (currentCard === 'spade') { ... }. Just a guess, but it takes into account the fact that the lesson checker is biased toward the pattern or response that is built into it, and may not take into consideration other possible working patterns.

Your pattern above is actually better suited to do-while where the condition is tested after each iteration.

const n = cards.length;
let x;
do {
  x = cards[Math.floor(Math.random() * n)]
} while(x !== 'spade');

If their expected solution includes an if statement, then,

const n = cards.length;
let x;
do {
  x = cards[Math.floor(Math.random() * n)]
  if (x === 'spade') break;
} while(true);

Again, purely speculating as what the solution pattern is meant to be. If you would care to share a link to the exercise, we could take a closer look.

Aside from my one little addition, I followed the instructions exactly. Here is the official solution code from the lesson alongside my solution;

Official solution

const cards = ['diamond', 'spade', 'heart', 'club']; // Write your code below let currentCard; while (currentCard !== 'spade') { currentCard = cards[Math.floor(Math.random() * 4)]; console.log(currentCard); }

My solution

const cards = ['diamond', 'spade', 'heart', 'club']; // Write your code below let currentCard; while (currentCard !== 'spade') { currentCard = cards[Math.floor(Math.random() * cards.length)]; console.log(currentCard); }

As you can see my solution matches almost exactly aside from using .length as the bound for the random-expression, strongly implying that the .length was what lead to the faulty diagnostic.

In this instance the data structure is static, not dynamic, with a known size of 4. It’s fair to believe the author didn’t see the need to poll the length property as part of the expected solution, so didn’t write a pattern to match that.

Bottom line, you’ll drive yourself mad if you spend any time questioning the SCT. Take it in stride. BTW, knowing what the expected solution is, get the green check mark, then change the code to what you like, and Run it again so it gets saved.

1 Like