Count the smiley faces

Posted at — Nov 7, 2019

Problem

Create a function counting how many valid smileys there are in an array.

Solution

The solution seems to need these functions:

Then loop over all smileys and split/check each of them.

function countSmileys(arr) { splitSmiley = smiley => smiley.split("") checkEyes = splittedSmiley => { validEyes = [':',';'] eye = splittedSmiley[0] return validEyes.includes(eye) } checkNose = splittedSmiley => { validNoses = ['-', '~'] nose = splittedSmiley[1] return validNoses.includes(nose) } checkMouth = splittedSmiley => { validMouths = [')','D'] mouth = splittedSmiley[splittedSmiley.length - 1] return validMouths.includes(mouth) } if (arr.length == 0) { return 0 } let counterSmileys = 0 arr.forEach(smiley => { const splittedSmiley = splitSmiley(smiley) const eyesOk = checkEyes(splittedSmiley) const mouthOk = checkMouth(splittedSmiley) let noseOk = true // if no nose it's ok if (splittedSmiley.length === 3) { // 3 means eyes, nose and mouth noseOk = checkNose(splittedSmiley) } if (eyesOk && noseOk && mouthOk) { counterSmileys += 1 } }) return counterSmileys } // test const smileys = [';]', ':[', ';*', ':$', ';-D'] countSmileys(smileys)

splitSmiley

splitSmiley = smiley => smiley.split("")

If the separator is "" the string is converted to an array of each of its characters.

checkEyes

checkEyes = splittedSmiley => {
        validEyes = [':',';']
        eye = splittedSmiley[0]
        return validEyes.includes(eye)
    }

Note you do need an explicit return keyword.

Make sure you understand the in operator. For arrays you must specify the index number instead of the index value which might be confusing if you’re used to other languages like Python or R. So this:

eyes in validEyes

would not work because it will return false. You have to use the includes method of Array.

checkMouth

checkMouth = splittedSmiley => {
        validMouths = [')','D']
        mouth = splittedSmiley[splittedSmiley.length - 1]
        return validMouths.includes(mouth)
    }

At first I used here but pop() changes the array its called upon. This results in bugs difficult to find.

countSmileys

let counterSmileys = 0

    arr.forEach(smiley => {
        const splittedSmiley = splitSmiley(smiley)
        const eyesOk = checkEyes(splittedSmiley)
        const mouthOk = checkMouth(splittedSmiley)
        let noseOk = true // if no nose it's ok

        if (splittedSmiley.length === 3) {
            // 3 means eyes, nose and mouth
            noseOk = checkNose(splittedSmiley)
        }

        if (eyesOk && noseOk && mouthOk) {
            counterSmileys += 1
        }
    })

A running counter for the number of smileys. Could have put these if statements in seperate functions to make things more clear.

counterSmileys is a let because it can change, if not I would have used const.

In the end it was too bad the tests on Codewars kept failing and I couldn’t see which tests failed exactly. This made it impossible to debug.

Alternative solutions

From the solutions with best practices this one was pretty readable. He created a set of rules as separate functions feeding them into a filter function like this:

const countSmileys = smileys =>
  smileys.filter(smiley =>
    smileyIsValid(smiley) &&
    smileyHasValidEye(smiley) &&
    smileyHasValidNose(smiley) &&
    smileyHasValidMouth(smiley)
  ).length

It almost reads like plain English.

Concepts

Functions in Javascript can be defined in multiple ways:

Regular function:

function () {}

Arrow function:

() => {}

Compared to a regular function an arrow function:

At first sight the syntax of the arrow function seems almost the same as for a regular function. But there are shortcuts if:

In this example I also took advantage of the scoping rules by defining helper functions like splitSmiley within the overarching countSmileys function:

The nested (inner) function is private to its containing (outer) function.