Free Code Camp Algorithmic Challenges Part 4

This is part 4 of a series in which I give solutions to the Free Code Camp Algorithmic Challenges. Have a look at part 1 here, part 2 here, and part 3 here. You can also look at the github repo with all my solutions here.

Don't look at this post until you've solved these challenges yourself! You've been warned. :)

Also, I'm using whatever libraries I feel like using (ramda and FolkTale in this post) and any new JavaScript features I like for all of these challenges, something you can't do at freecodecamp.com.

Ok, with all that out of the way, Let's get on to the next challenges. :)

Repeat a string repeat a string

Okie dokie, we need to write a function that takes a string str and a number n and returns a string that repeats that string n times. If n is less than zero, we return an empty string.

The code, it goeth thusly:

const  
    R = require('ramda'),
    {Left, Right} = require('data.either'),
    notNeg = n => n < 0 ? Left('') : Right(n)

module.exports = (str, n) =>  
    notNeg(n)
        .map(R.range(0))
        .map(R.map(() => str))
        .map(R.join(''))
        .fold(x => x, x => x)

So, I've not written about Either yet (I'm using FolkTale's implementation) but it's pretty nifty. notNeg uses it to return either a Left('') (a container type that holds an empty string) or a Right(n) (a container type that holds our number n).

Both Left and Right have the same Either-ish interface so we can safely call the same methods whichever we get.

Just like JavaScript arrays, both Left and Right have map methods. Right's map works just like an array's map. But Left's map doesn't, it just returns a Left identical to itself. In other words, it doesn't apply the given function to it's contents.

Or, for any function f, Left(x).map(f) === Left(x) and Right(x).map(f) === Right(f(x)).

We can use this to our advantage. If notNeg(n) returns a Left(''), all the map calls return a Left(''). We just keep our empty string, which is what we want.

However, if notNeg(n) returns a Right(n), then our map calls actually have an effect. We first return an array of numbers from 0 to n - 1, in order. Then we map over that array so each element is str. Finally we join that array to get our repeated string.

fold acts like map too, but it only returns the contents of the Either, not a Left or Right container type. You could say it maps and then unboxes.

The first function given to fold is applied if the Either is a Left, the second function given to fold is applied if the Either is a Right. In both cases I've just given it the identity function.

The upshot is that we either get an empty string or we get a string that is a repeat of the original.

Truncate a string

Ok, we are asked to write a function that takes a string str and a number n. We then a return a truncated version of str whose length is not greater than n.

One other thing, if the string is already short enough we leave it alone; No truncating. If it's not we replace the last 3 characters at the end with '...'.

Oh, and one other other thing. If n <= 3, then we don't replace the 3 characters with '...', we just tack '...' onto the end.

Ok, now let's get to the code:

const  
    shortEnough = (str, n) =>
        n >= str.length ? Left(str) : Right(str),
    conditionalSlice = n => str =>
        R.slice(0, n <= 3 ? n : n - 3, str)

module.exports = (str, n) =>  
    shortEnough(str, n)
        .map(conditionalSlice(n))
        .map(R.concat(R.__, '...'))
        .fold(x => x, x => x)

I've used an Either again! It makes sense since we're either going to leave the string alone or we're going to transform it. If we transform it, we conditionally slice it based on the requirements I laid out above. Then we tack on the '...' and fold out just like before.

Ok, I think that's enough for a single post. See you next week.

Happy coding. :)

Looking for a software developer?