Demo: Demo: Solving Problems with JavaScript

An introduction to strategic thinking as it applies to JavaScript coding.

Scroll down...

Content

Resources

Comments

JavaScript programmers all suffer from a secret, shared pain. And while they might wear a smile in polite company, do not believe it—this is but a mask to conceal a great sorrow. Yet today, we shall dislodge their psychic thorn, freeing them to code in peace once more.

Some programming languages have but a single falsy value: False. Others may have one or two. JavaScript has six. And every JavaScript developer lives in fear that one day she will forget one of these values, perhaps accidentally supplanting it with another truthy value, thus destroying her entire reputation and career. What are they again? Nil, 0, undefined, the string “Pepper Boy”, and 38, right? Right!? Never again.

Today, we are going solve this problem once and for all by creating an application so incredible that it is likely to be hard-coded into the Hacker News front page HTML in order to spare their algorithm from the fruitless calculations, performed in vain, to reprioritize us. We will build a very simple flash card game that any paranoid JavaScripter can use to extinguish this universal anxiety for good. It should also prove to be a formidable test of our skills thus far. We are going to build objects, manipulate arrays, craft functions, and even toy with a bit of ES6 syntax.

We will first break down our application into a few subproblems. After we have conquered these, we will compose our sub-solutions together into our final application.

1. Getting User Input

At some point, we will need to ask our player if they think a particular value is truthy or falsy. Luckily, all browsers come with a very simple command to retrieve a boolean value from the user. I'm sure you’ve seen something like this before:

Confirm example

(Don't worry they'll keep your data around forever just in case.)

Here is how we can make something like this ourselves:

confirm("Are you sure you want to deactivate your Facebook?")
//=> false

// You can simply save the return value to use later
var isTraitor = confirm("Are you sure you want to deactivate your Facebook?")

// Or perhaps do something more insidious
var betrayalFactor = 0
while (confirm("Are you " + "really ".repeat(betrayalFactor) + "sure you want to deactivate your Facebook?")) {
        betrayalFactor++
}
alert("Your loyalty will not be forgotten. - MZ")

Keep in mind the methods like alert and confirm only exist when JavaScript is run in the browser. These are methods provided by the browser’s API. You already know how to look at the implementation of a function, so you may find this interesting:

Native Code

You can't actually look at the implementation of browser-provided functions because it turns out they aren't written in JavaScript. The browsers merely provide a friendly JavaScript-friendly window into their[native code] . The more you know 🌈.

2. Shuffling Arrays

To make our game challenging to the user, we must show them both falsy and truthy values. If our quiz consisted of entirely falsy values, then the user would soon pick up on the pattern and end up learning nothing. We will combine these two sets of values into a single array and then shuffle this new array. If the array was in the same order each time, then the user would soon pick up on the pattern and end up learning nothing. We must keep our user on their toes, revealing no pattern from which they might exploit our game and safeguard their ignorance.

So, here are our arrays:

const falsyValues = [
    "false",
    "0",
    "-0",
    "null",
    "undefined",
    "NaN",
    "\"\""
]

const truthyValues = [
    "\"Goodbye, World!\"",
    "1",
    "9000",
    "\"false\"",
    "true",
    "[]",
    "{name: \"Simple Simon\"}"
]

ES6 Moment: Const

const is a new type of keyword in ES6. It stands for constant. We use it instead of var (or the other new assignment keyword, let) to tell JavaScript that we never intend to change this particular value. In this case, we don't want someone coming in and swapping out our falsyValues with an entirely new array. This would spoil the integrity of our game, and we’d probably only be on the front page of Hacker News for a few days.

You will get an error if you try to assign a new value to a variable you created using const. You will see something like, TypeError: Attempted to assign to read-only property. This will help you make sure you don't accidentally overwrite a value that is not supposed to be overwritten. It's a good practice to assign all of your new variables using const. You can always change it to a let or var later on. This can also help the JavaScript engine better optimize your code.

The Truthy Shuffle

In order to write our shuffle function, we will employ our first algorithm. “Algorithm”, as I’m sure you already know, is just a fancy term for  series of steps to get something done. In our case, we seek a series of steps to shuffle an array. The algorithm we going to use is called the Fisher-Yates shuffle, originally conceived in the 1930’s by two shuffling statisticians.

Imagine a deck of cards in need of shuffling. First, you place each card in a row on the table (imagine a longer table). To shuffle the cards, you simply select a random card from the table and place it into your hand. Continue to do this until all the cards are in your hand.

That’s it. It's not too complicated. Feel free to try the next time you're playing cards with friends. You will be sure to impress.

Despite having an imposing, hyphenated name, it's a relatively simple algorithm. The main feature of the algorithm is that a card is only selected once. Now let's see what this looks like in code:

// First, we define a new function that takes an array as an argument
function shuffleArray(array) {

  // We loop through the array from the length down to 1
  for (let i = array.length; i > 0; i--) {

    // Here we are storing a random number between 0 and i.
    const randomIndex = Math.floor(Math.random() * i);

    // We swap the value at the current index with
    // the value at the random index.
    [array[i - 1], array[randomIndex]] = [array[randomIndex], array[i - 1]];
  }

}

This is certainly a little less clear than in our card fantasy. But think of it like this, our card pool is determined by the range of potential random numbers, and each time we loop, we reduce that range by one. Our hand is represented by an increasing allotment at the end of the array, at the indices beyond Math.random’s range. So both the pool and the hand are represented implicitly rather than physically. Finally, in order to ensure that each value is selected exactly one time, we swap the value at the random index with the value at the right-most index of the pool range, which will momentarily be the left-most index of our hand range—once the i is next decremented.

Please try playing the code in a JS Bin, throwing some console.logs around. This should help make things a little clearer.

ES6 Moment: Let

for (let i = array.length; i > 0; i--) { ... }

Although I mentioned let earlier, we haven't seen it used until now. It’s best to simply think of it as ES6’s var. var has some gnarly edge cases and quirky behaviors. Though we will eventually explore what those are, let may save you from headaches until then.

ES6 Moment: Destructuring Assignment

[array[i - 1], array[randomIndex]] = [array[randomIndex], array[i - 1]];

This may have seemed like an odd bit of code. Here we are using what is called Destructuring Assignment. Here's an example with a bit less noise to help you see what's going on.

// Here a couple of variables
 let a = 3
let b = 8

// Here we use destructuring to reassign them
[a,b] = [1,2]
a //=> 1
b //=> 2

// We can even use it to swap values
[a,b] = [b,a]
a //=> 2
b //=> 1

// You can assign new variables this way as well
let [c,d,e] = [4,5,6]
 c //=> 4
 d //=> 5
 e //=> 6

It's a really lovely technique for swapping the values of variables. Check out ES6 Features to see some more examples and to see how you would have to do this without destructuring.

3. Objectification

Now we can easily create the jumbled array for our quiz:

var quizList = truthyValues.concat(falsyValues)
shuffleArray(quizList)
//=> ["true", "false", "null", "9000", ...]

But there's a big problem. Our code has no way of telling the falsy values from the truthy ones. The string representations of these values do not hold this additional information. When the primitive data types start to feel limiting, and you find yourself yearning for additional complexity, try turning to Objects.

What we really want, is not a list of strings, but a list of questions. So let's make a function that takes a string representation and it’s truthy or falsy value and returns an object.

function questionMachine(stringRepresentation, booleanValue) {
    return {
    stringRepresentation: stringRepresentation,
    booleanValue: booleanValue
    }
}

Now we can transform our value arrays using map.

let truthyObjects = truthyValues.map(function(string) {
    return questionMachine(string, true)
})
let falsyObjects = falsyValues.map(function(string) {
    return questionMachine(string, false)
})

With two arrays of objects rather than strings, we have the right data to create our shuffled test array. In fact, it looks like we have solved all of our subproblems and are finally ready to create our game-changing application.

4. Actually Building the Thing

In order to give the player feedback at the end of the quiz, we will need to store the results of their answers. To keep things simple, let’s add a correct property to the question object.

function questionMachine(stringRepresentation, booleanValue) {
    return {
    stringRepresentation: stringRepresentation,
    booleanValue: booleanValue,
    correct: false // Our new property
    }
}

There could be an argument for providing our correct property with a default value of null, to represent its unanswered state. But we are really not going to need that level of detail for our game, so—in the spirit of YAGNI—we will start our questions out as false.

Now we just have to iterate through our shuffled question array, asking the user to guess the truthy or falsy value of each one.

// First a prompt to explain our somewhat hacky feedback mechanism
alert("Press 'OK' for Truthy and 'Cancel' for Falsy")

// And here is the loop over the array
quizArray.forEach( (element) => {

  // First, we get the user's answer.
  const answer = confirm(element.stringRepresentation)

  // Then we see if the boolean value of the element
  // matches their answer, assigning the results of the comparison
  // to the object's 'correct' property
  element.correct = answer == element.booleanValue
})

Finally, let's display the results to our player.

alert(quizArray.map( (element) => {
  return`${element.stringRepresentation}: ${element.correct ? 'Correct' : 'WRONG'}`
}).join("\n"))

Here, we are turning our array of objects into an array of strings using map and then joining them, separated by new lines, into a single string using join. This entire operation is inside of a call to alert, which will display the output to the user in yet another pop-up.

ES6 Moment: String Interpolation

In vanilla JavaScript, you have to manually concatenate strings.

"My name is " + nameVariable + "!"

This is kind of ugly. It can become difficult to visually parse, what with all of the plus signs and double quotes cluttering up your screen. ES6 string interpolation allows you to insert variables directly into the string.

My name is ${nameVariable}!

Strings that use interpolation must be demarcated with backticks (`) rather than quotes. The interpolated expressions must be surrounded by curly braces prefixed with the dollar sign. It's a bit odd looking at first, but once you get used to it, it's much nicer than patching together a whole bunch of values using +.

Wrapping Up

We may have a slightly exaggerated the potential viral impact of our application. While you might have gone through this demo expecting fame and fortune, we hope you do not leave entirely empty-handed. Though exceedingly simple in some regards, this application exhibits some of the major components of real-world applications. We have a data model, user input transforming that data, and the presentation of the data back to the user.

These components will inevitably grow in complexity alongside the requirements of the application, yet their essence will remain intact. The concept of modeling data will never be discarded for some more advanced or “professional” abstraction. In fact, our array of questions could be transplanted into a more complicated user-interface, with animations and asynchronous communication with a server, and it would hold up just fine.

To play the game, or perhaps steal the code and submit it to Hacker News, check out JS Bin with the finished code.



Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

There are no additional resources for this lesson just yet!

Sorry, comments aren't active just yet!

Next Lesson: Test Yourself: JavaScript Basics