This Week in Dev

JavaScript - Classes, Functions, and Recursion

Great practice using classes in JavaScript, and working with recursion to solve an insertion problem.

Atbash Cipher

// extend the string object to filter out whitespace and shift to lower case
String.prototype.filterInput = function() {
    return this.replace(/\W/g, '').toLowerCase();
};

// extend the array object to return a string grouped into segments of five
Array.prototype.groupIntoFives = function() {
    return this.reduce((remit, char, index) =>
        index % 5 === 0
            ? remit += ' ' + char
            : remit += char
    , '').trim();
};

function atbashEncodeChar(c) {
    // return number as-is
    if (!isNaN(c))
        return c;

    // get zero-index index of the input letter (ie. a = 0, z = 25)
    const zeroIndex = c.charCodeAt(0) - 'a'.charCodeAt(0);

    // get the transposed letter (bounded by size of alphabet)
    const newLetter = (26 - zeroIndex + 25) % 26;

    // transpose back into ASCII range (ie. a = 97, z = 122)
    return String.fromCharCode(newLetter + 'a'.charCodeAt(0));
}

export const encode = input =>
    input.filterInput()
        .split('')
        .map(c => atbashEncodeChar(c))
        .groupIntoFives();

High Scores

export class HighScores {
    constructor(input) {
        this.scores = input;
    }

    get latest() {
        return this.scores[this.scores.length - 1];
    }

    get highest() {
        return Math.max(...this.scores);
    }

    get top() {
        // build a sorted array
        return this.scores.reduce((array, value) => {
            if (array.length === 0)                    // new array
                return [value];

            else if (value >= array[0])                // belongs at head of array
                return [value, ...array];

            else if (value <= array[array.length - 1]) // belongs at tail of array
                return [...array, value];

            // value belongs somewhere in the middle of the array
            return function findInsertionIndex(value, segOne, segTwo) {
                // BASE CASE - value fits between segment 1 and segment 2
                if ((value === segOne[segOne.length - 1] || value === segTwo[0])
                 || (value  <  segOne[segOne.length - 1] && value  >  segTwo[0]))
                    return [...segOne, value, ...segTwo];

                // RECURSION CASE - test insertion at the next index
                // remove first element from segment 2 and add it to end of segment 1
                return findInsertionIndex(value, [...segOne, segTwo.shift()], segTwo);
            } (value, array.slice(0, 1), array.slice(1))
        }, [])
            .slice(0, 3); // take first 3 scores
    }

    get report() {
        return this.latest >= this.highest
            ? `Your latest score was ${this.latest}. That's your personal best!`
            : `Your latest score was ${this.latest}. ` +
              `That's ${this.highest - this.latest} short of your personal best!`;
    }
}