Free Code Camp Algorithmic Challenges Part 10

This is part 10 of a series in which I give solutions to the Free Code Camp Algorithmic Challenges. Don't look at this post until you've solved these challenges yourself! You've been warned. :)

Here are the links for parts 1, 2, 3, 4, 5, 6, 7, 8, and 9. You can also look at the github repo with all my solutions.

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

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

DNA Pairing

Here we're asked to take a string of A's, T's, C's, and G's (ala DNA) and match them with their pairs. A's go with T's and C's go with G's. The result will be an array of base pairs. For example, "ATCGA" gets us [["A","T"],["T","A"],["C","G"],["G","C"],["A","T"]].

Here's the code:

const  
    R = require('ramda')

const  
    dnaMap = {
        A: 'T',
        T: 'A',
        C: 'G',
        G: 'C'
    }

module.exports =  
    R.pipe(
        R.split(''),
        R.map(x => [x, dnaMap[x]]))

First I make a map of the relationship between A's and T's, and C's and G's with dnaMap.

Then our function takes the given string and splits it up into characters. A map call takes each character and returns an array of the matched pairs.

Missing letters

In this exercise we're given a string of letters and asked to provide the letter missing in the range. If given 'abcefg' we return 'e'. If given 'xz' we return 'y', and so on. If there are no gaps, we return undefined.

Just for fun, I made a function that would take a string of letters not in alphabetical order and still find the missing letter. Also, it will return a string of missing letters if more than one letter is missing. For example, 'klmnoabdefgh' gets us 'cij' and 'rqv' gets us 'stu'.

Here's the code:

module.exports =  
    R.pipe(
        R.split(''),
        R.map(x => x.charCodeAt(0)),
        R.sort(R.subtract),
        R.aperture(2),
        R.filter(xs => xs[1] - xs[0] > 1),
        R.chain(xs => R.range(xs[0] + 1, xs[1])),
        R.map(String.fromCharCode),
        xs => xs.length === 0
            ? undefined :
            xs.join(''))

First we split the string into an array of characters, map each character to its character code, and sort the character codes.

R.aperture breaks up an array into chunks, each chunk is of a size you specify. Also the last element of each chunk is repeated in the first element of the next chunk.

So R.aperture(2, [1, 2, 3, 4]) returns [[1, 2], [2, 3], [3, 4]] and R.aperture(3, [1, 2, 3, 4, 5, 6]) returns [[1, 2, 3], [3, 4, 5], [5, 6]] and so on.

We call R.aperture(2) on our array of character codes and filter out the arrays whose first and second elements only differ by 1 or 0. For example [[101, 102], [102, 106], [106, 107]] would be transformed to [[102, 106]]. This filters out any characters from our original array that had no gaps.

Next, we map each remaining array, returning the range of numbers in between the gaps. For example, if the array is [[102, 106], [110, 120]] we'd transform it to [[101, 102, 103, 105], [111, 112, 113, 114, 115, 116, 117, 118, 119]]

But we also want to flatten this 2D array into a 1D array so we use R.chain instead of R.map, which maps and flattens at the same time. Our example array is actually transformed into [101, 102, 103, 105, 111, 112, 113, 114, 115, 116, 117, 118, 119].

Next, we turn each character code back into a letter. At this point, if there are no gaps in the original string we'd have an empty array, so we use a ternary if to return either undefined or to return a joined array (i.e. the string of missing characters we needed to return).

Ok, I think that's probably enough for one post. See ya next time kiddies. :)

Looking for a software developer?