JavaScript challenges

JavaScript Challenges

Let's work through some common coding challenges using JavaScript.

Gideon Kreitzer
2022/1/30
  1. FizzBuzz
  2. Palindrome
  3. Flashcards

FizzBuzz

Fizz Buzz is a word game for children that is played in a group, and teaches them about division. However, the game also made its way into software engineering interviews under the moniker of "FizzBuzz" as a helpful gauge in measuring some fundamental analytical and language-skill metrics.

It goes something like this: given an arbitrary integer ℕ, write logic that sequentially prints the range of numbers in ℕ starting at 1, but prints "Fizz" if ℕ is divisible by 3, "Buzz" if ℕ is divisible by 5, or "FizzBuzz" if ℕ is divisible by both 3 and 5.

There are naturally a myriad of ways in which the FizzBuzz challenge may be approached. I'll touch on two possible approaches here, one using a for...of loop, and one using a traditional for loop. We'll set the default number range for both solutions to 50.

/** @function
 * @name fizzBuzz
 * @summary Solves FizzBuzz using a 'for..of' loop.
 * @param { number } n - Upper limit for the range.
 * @returns { (number|string) }
 */
const fizzBuzz = (n = 50) => {
    /* Turn input (n) into a range of integers 
     * that takes the shape of an iterable. */
    const iterable = [...Array(n).keys()]
    /* Slice out the first element in the new 
     * Array (0) so that 1 is its first value. */
    const range = iterable.slice(1, iterable.length)
    // Iterate through the range and play FizzBuzz!
    for (const i of range) {
        if (i % 15 === 0) {
            console.log('FizzBuzz')
            continue
        }
        if (i % 3 === 0) {
            console.log('Fizz')
            continue
        }
        if (i % 5 === 0) {
            console.log('Buzz')
            continue
        }
        console.log(i)
    }
}
/** @function
* @name fizzBuzz
* @summary Solves FizzBuzz using a 'for' loop.
* @param { number } n - Upper limit for the range.
* @returns { (number|string) }
*/
const fizzBuzz = (n = 50) => {
    for (let i = 1; i < n; i++) {
        if (i % 15 === 0) console.log('FizzBuzz')
        else if (i % 3 === 0) console.log('Fizz')
        else if (i % 5 === 0) console.log('Buzz')
        else console.log(i)
    }
}

Palindrome

A palindrome is a symmetrical sequence of letters or digits in a word, sentence, or number that remains unchanged whether read from left to right, or from right to left. Punctuation marks are ignored.

We'll create a function that checks whether a given character set is a palindrome. The first function will use built-in Array methods, in the second function we'll solve the challenge with the help of a for loop, and in the third we'll use yet another approach using a for of loop.

/** @function
 * @name palindrome
 * @summary Check if string is a palindrome using built-in methods.
 * @param { string } str - String to be validated.
 * @returns { boolean }
 * @example
 * palindrome('1881') // true
 * palindrome('Madam, I'm Adam.') // true
 * palindrome('Is this a palindrome?') // false
 */
function palindrome(str) {
    const invalid = /[\W]/g
    const normalized = str.toLowerCase().replace(invalid, '')
    const reversed = normalized.split('').reverse().join('')
    return normalized === reversed
}
/** @function
* @name palindrome
* @summary Check if string is a palindrome using a for loop.
* @param { string } str - String to be validated.
* @returns { boolean }
*/
function palindrome(str) {
    // declare a placeholder array for our loop to work on
    let reversed = []
    // define a regex that looks for all non-alphanumeric characters
    const invalid = /[\W]/g
    // coerce the input string to lowercase and sanitize against our regex
    const normalized = str.toLowerCase().replace(invalid, '')
    // create a loop to populate the placeholder array
    for (let i = 0, j = normalized.length; i < j; i++) {
        // traverse the benchmark string from right to left
        reversed.push(normalized[j - i - 1])
    }
    // convert the two proper arrays into strings
    reversed = reversed.join('')
    // compare the original string with the inverted string
    return normalized === reversed
}
/** @function
* @name palindrome
* @summary Check if string is a palindrome using a for..of loop.
* @param { string } str - String to be validated.
* @returns { boolean }
*/
function palindrome(str) {
    let reversed = [];
    const invalid = /[\W]/g
    const normalized = str.toLowerCase().replace(invalid, '')
    // turn the string into a proper Array
    const benchmark = [...normalized]
    // use a for..of loop
    for (const char of normalized) {
        // use Array.pop()
        reversed.push(benchmark.pop())
    }
    reversed = reversed.join('')
    return normalized === reversed
}

Flashcards

Flashcards are a deck of cards with each card containing a question on the one side and the answer on the other side. They are typically used in a gamified fashion where a presenter will show the "question" side of a card to one or more people and they will attempt to provide the correct answer in order to proceed to the next card in the stack. There may naturally be variations on the theme, but this is a sufficient definition for our purposes.

To build a rudimentary flashcard game, we'll need some markup to boot. This is a no-frills solution and certainly one of a plethora of approaches. To keep the answer-vetting logic simple, the flashcards' theme will be basic math. Let's dive in without further ado.

 <!-- the flashcard markup -->
    
<html>

    <head>
        <script src="./flashcards.js"></script>
    </head>

    <body>
        <h1>Flashcards</h1>
        <h2>Arithmetic practice.</h2>
        <h3 id="card">CARD</h3>
        <form>
            <div>
                <label for="answer">Answer</label>
                <input type="text" id="answer">
            </div>
            <button>Submit</button>
        </form>

    </body>

</html>

That's a wrap for the markup. Next, the logic:

// data intake is static for this example
const data = [
    { id: 1, question: '3²', answer: 9 },
    { id: 2, question: '3 + 7', answer: 10 },
    { id: 3, question: '12 - 6', answer: 6 },
]

/** @function
 * @name flashcards
 * @summary Simple, functional flashcard game.
 * @param { array } cards - Objectified cards.
 * @returns { void }
 */
function flashcards(cards) {

    // variable declarations
    let currentCard = null,
        userAnswer = null,
        deck = new Array();

    const card = document.getElementById('card'),
        answer = document.getElementById('answer'),
        form = document.getElementsByTagName('form')[0];

    // startup logic
    function init() {
        // add cards to the deck
        cards.forEach(card => deck.push(card))
        // draw the first card
        displayCard()
    }

    // card drawing
    function displayCard() {
        if (deck.length > 0) {
            currentCard = deck.shift()
            card.textContent = currentCard.question
        } else {
            confirm('Done!\nWant to start over?') ?
                init() :
                null
        }
    }

    // manage user interaction
    function handleAnswer(event) {
        event.preventDefault()
        // coerce input value to number
        userAnswer = +answer.value
        if (currentCard.answer === userAnswer) {
            alert('Correct!')
            displayCard()
        }
        else {
            alert('Try again!')
            answer.value = ''
        }
    }

    // capture the event that bubbles from button
    form.addEventListener('submit', handleAnswer)

    // start the game
    init()
}

// defer game initialization to DOM's ready event
document.addEventListener('DOMContentLoaded', flashcards.bind(this, data))

A live example of the game lives here.

Credits
Photo by Florian Schönbrunner
Tags
javascript challenges practice