Five ways to crack the DNA pairing challenge in JavaScript

Overview

Trust me when I say that Computer Science is awesome and amazing! There is almost always more than one way to come to a solution to a given problem.

In this tutorial, we will learn how to solve the Free Code Camp DNA Pairing Challenge in five different ways.

Problem statement

Each DNA molecule is made up of two strands, and there are four nucleotides present in DNA: Adenine, Cytosine, Thymine, and Guanine. Each of the nucleotides on one side of the strand pairs up with a specific nucleotide on the other side of the strand. For example, if there’s a G on one side of the strand, there will always be a C on the other, and vice versa. If there’s a T on one side of the strand, there will always be an A on the other, and vice versa.

In this challenge, we are interested in finding the missing nucleotides on the other side of the strand.

For example, the string GCG represents one strand of the DNA. The missing pair elements on the other strand are CGC. Hence, as a result, we will return a 2D array made of arrays of pair elements grouped into one encapsulating array: [[“G”, “C”], [“C”,“G”],[“G”, “C”]].

The DNA strand is missing the pairing element. Take each character, get its pair, and return the results as a 2D array. Base pairs are a pair of AT and CG. Match the missing element to the provided character. Return the provided character as the first element in each array. For example, for the input GCG, return [["G", "C"], ["C","G"],["G", "C"]]. The character and its pair are paired up in an array, and all the arrays are grouped into one encapsulating array.

function pairElement(str) {
  return str;
}

pairElement("GCG");

Provided test cases

  • pairElement(“ATCGA”) should return [["A","T"],["T","A"],["C","G"],["G","C"],["A","T"]]
  • pairElement(“TTGAG”) should return [["T","A"],["T","A"],["G","C"],["A","T"],["G","C"]]
  • pairElement(“CTCTA”) should return [["C","G"],["T","A"],["C","G"],["T","A"],["A","T"]]

Understanding the problem

As we may have read in the challenge description above, the goal of this exercise is to return the missing strand in a 2D array. In biology class, we learned about DNA base pairs (Need a refresher? Wikipedia is your friend). They are A-T and C-G, and they go both ways. So, every time we have:

  • An “A” string, we return the array [‘A’, ‘T’].
  • A “T” string, we return the array [‘T’, ‘A’].
  • A “C” string, we return the array [‘C’, ‘G’].
  • A “G” string, we return the array [‘G’, ‘C’].
Given an input string of GCG, return a 2D array of [[“G”, “C”], [“C”,“G”],[“G”, “C”]]
1 of 4

Code

1. for loop and if statement

For this solution, we will loop over the parameter that is passed to the function and use an if statement to return the correct pair.

function pairElement(str) {
  // Step 1. Declare the variable of type array that will encapsulate other paired arrays
  const arrDNA = [];

  // Step 2. Create the FOR loop with initializer less then str.length
  for (let i = 0; i < str.length; i += 1) {
    // Step 3. Use if statement to evaluate baise pair and push it to arrDNA
    if (str[i] === 'A') arrDNA.push([str[i], 'T']);
    if (str[i] === 'T') arrDNA.push([str[i], 'A']);
    if (str[i] === 'C') arrDNA.push([str[i], 'G']);
    if (str[i] === 'G') arrDNA.push([str[i], 'C']);
  }

  /* Here "GCG"'s length equals 3
      For each iteration: i = 0 and arrDNA = [[str[i], 'corresponding pair']]
      First iteration:  i = 0        arrDNA = [['G', 'C']]
      Second iteration: i = 1        arrDNA = [['G', 'C'], ['C', 'G']] 
      Third iteration:  i = 2        arrDNA = [['G', 'C'], ['C', 'G'], ['G', 'C']]         

    End of the FOR Loop*/

  // Step 4. Return the 2D array
  return arrDNA;
}

pairElement("GCG");

Without comments:

function pairElement(str) {
  
  const arrDNA = [];

  for (let i = 0; i < str.length; i += 1) {
    
    if (str[i] === 'A') arrDNA.push([str[i], 'T']);
    if (str[i] === 'T') arrDNA.push([str[i], 'A']);
    if (str[i] === 'C') arrDNA.push([str[i], 'G']);
    if (str[i] === 'G') arrDNA.push([str[i], 'C']);

  }

  return arrDNA;
}

pairElement("GCG");
function pairElement(str) {
const arrDNA = [];
for (let i = 0; i < str.length; i += 1) {
if (str[i] === 'A') arrDNA.push([str[i], 'T']);
if (str[i] === 'T') arrDNA.push([str[i], 'A']);
if (str[i] === 'C') arrDNA.push([str[i], 'G']);
if (str[i] === 'G') arrDNA.push([str[i], 'C']);
}
return arrDNA;
}
console.log(pairElement("GCG"));

2. The for loop, charAt() method, and if statement

In this solution, we will make use of the traditional for loop and if statements once more, in combination with the String object’s charAt() method. The charAt() method (String.prototype.charAt()) returns the character at the specified index in a string.

function pairElement(str) {
  // Step 1. Create an empty array that will encapsulate other paired arrays
  const arrDNA = [];

  // Step 2. Iterate through the str with a FOR loop 
  for (let i = 0; i < str.length; i += 1) {
    // Step 3. Use if statement to evaluate base pair and push it to arrDNA
    
    // If the current str character is X create an array of current str with its corresponding pair and push the array to arrDNA
    
    if (str.chartAt(i) === 'A') // if A
      arrDNA.push([str[i], 'T']); // ...push [A - T]
    else if (chartAt(i) === 'T') // if T 
      arrDNA.push([str[i], 'A']); //...push [T - A]
    else if (chartAt(i) === 'C') // if C
      arrDNA.push([str[i], 'G']); // ...push [C - G]
    else if (chartAt(i) === 'G') // if G
      arrDNA.push([str[i], 'C']); // ...push [G - C]

  }

  // Step 4. Return the 2D array
  return arrDNA;
}

pairElement("GCG");

Without comments:

function pairElement(str) {
  const arrDNA = [];

  for (let i = 0; i < str.length; i += 1) {
    
    if (str.chartAt(i) === 'A') 
      arrDNA.push([str[i], 'T']); 
    else if (chartAt(i) === 'T') 
      arrDNA.push([str[i], 'A']); 
    else if (chartAt(i) === 'C') 
      arrDNA.push([str[i], 'G']); 
    else if (chartAt(i) === 'G') 
      arrDNA.push([str[i], 'C']); 

  }

  return arrDNA;
}

pairElement("GCG");
function pairElement(str) {
const arrDNA = [];
for (let i = 0; i < str.length; i += 1) {
if (str.chartAt(i) === 'A')
arrDNA.push([str[i], 'T']);
else if (chartAt(i) === 'T')
arrDNA.push([str[i], 'A']);
else if (chartAt(i) === 'C')
arrDNA.push([str[i], 'G']);
else if (chartAt(i) === 'G')
arrDNA.push([str[i], 'C']);
}
return arrDNA;
}
console.log(pairElement("GCG"));

3. for...of

The for...of creates a loop iterating over iterable objects (built-in string, array, and array-like objects).

function pairElement(str) {
  // Step 1. Create an empty array that will encapsulate other paired arrays
  const arrDNA = [];

  // Step 2. Create an object of base pair
  const basePair = {
    'A': 'T',
    'T': 'A',
    'C': 'G',
    'G': 'C'
  }

  // Step 3. Iterate through the str with a for of loop 
  for (const letter of str) {
    // Step 4. Create an array of letter with its corresponding pair and  push to arrDNA
    arrDNA.push([letter, basePair[letter]]);    
  }
  
  // Step 5. Return the 2D array
  return arrDNA;
}

pairElement("GCG");

Without comments:

function pairElement(str) {
  const arrDNA = [];

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

  for (const letter of str) {
    arrDNA.push([letter, basePair[letter]]);    
  }
  
  return arrDNA;
}

pairElement("GCG");
function pairElement(str) {
const arrDNA = [];
const basePair = {
'A': 'T',
'T': 'A',
'C': 'G',
'G': 'C'
}
for (const letter of str) {
arrDNA.push([letter, basePair[letter]]);
}
return arrDNA;
}
console.log(pairElement("GCG"));

4. split and map

Let’s try to resolve this problem using the String.prototype.split() and the Array.prototype.map() method. The String.prototype.split() method (split()) is used to convert a string into an array. The map() method creates a new array with the results of calling a function for every array element.

function pairElement(str) {
  // Step 1. Create an object of base pair
  const basePair = {
    'A': 'T',
    'T': 'A',
    'C': 'G',
    'G': 'C'
  }
   // Step 2. convert the str into an array with split and store the result into arrStr variable
  const arrStr = str.split('');

  /* Step 3. Map through the arrStr and return an array of current value and it baise
  Keep the result of mapping under arrDNA variable
  */
  const arrDNA = arrStr.map(letter => [letter, basePair[letter]])
  
  // Step 4. Return the 2D array
  return arrDNA;
}

pairElement("GCG");

Without comments:

function pairElement(str) {
  const basePair = {
    'A': 'T',
    'T': 'A',
    'C': 'G',
    'G': 'C'
  }
  const arrStr = str.split('');

  const arrDNA = arrStr.map(letter => [letter, basePair[letter]])
  
  return arrDNA;
}

pairElement("GCG");

Or, even better, we use the split() map() method in one line:

function pairElement(str) {
  const basePair = {
    'A': 'T',
    'T': 'A',
    'C': 'G',
    'G': 'C'
  }

  return str.split('').map(letter => [letter, basePair[letter]]);
}

pairElement("GCG");
function pairElement(str) {
const basePair = {
'A': 'T',
'T': 'A',
'C': 'G',
'G': 'C'
}
return str.split('').map(letter => [letter, basePair[letter]]);
}
console.log(pairElement("GCG"));

5. Split, forEach, and switch

In this solution, we will use the split(), forEach(), and switch methods. We have already discussed the split() method in another solution above. Let’s talk a bit about the remaining two:

  • array.forEach(): This method executes a provided function once for each array element.
  • switch: This method is similar to the if statement, but it gives a more descriptive way to compare a value with multiple variants.
function pairElement(str) { 
 // Step 1. Create an empty array that will encapsulate other paired arrays
  const arrDNA = []; 

  // Step 2. convert the str into an array with split and store the result into arrStr variable
 const arrStr = str.split('');

 // Step 3. Loop through arrStr using forEach
  arrStr.forEach(x => {
    /* Step 4. Use switch statement to test x and push the corresponding array to arrDNA */
   switch (x) {
     case "G": // in case x = G
      arrDNA.push(["G","C"]); // ...push ["G","C"] to arrDNA
      break // break tells the script to run from the case where the criterion is met      
     case "C":   
      arrDNA.push(["C","G"]);
      break;        
     case "T":
      arrDNA.push(["T","A"]);
      break;       
     case "A":
      arrDNA.push(["A","T"]);
      break;       
   }    
 });

// Step 5. Return the 2D array
 return arrDNA;
}
pairElement("GCG");

Without comments:

 function pairElement(str) { 
   const arrDNA = []; 

  const arrStr = str.split('');

   arrStr.forEach(x => {

    switch (x) {
      case "G": 
       arrDNA.push(["G","C"]);
       break       
      case "C":   
       arrDNA.push(["C","G"]);
       break;        
      case "T":
       arrDNA.push(["T","A"]);
       break;       
      case "A":
       arrDNA.push(["A","T"]);
       break;       
    }    
  });

  return arrDNA;
}
pairElement("GCG");

Wrap up

That is it for this tutorial. We used five different ways to solve the DNA pairing challenge that is available on FreeCodeCamp.

Which of these solutions appeals to you the most? Do you have any other solutions in mind? Share them with us! If I had to choose just one solution from all the ones mentioned above, I’d go for the 3rd one that makes use of for...of.

function pairElement(str) {
const arrDNA = [];
const arrStr = str.split('');
arrStr.forEach(x => {
switch (x) {
case "G":
arrDNA.push(["G","C"]);
break
case "C":
arrDNA.push(["C","G"]);
break;
case "T":
arrDNA.push(["T","A"]);
break;
case "A":
arrDNA.push(["A","T"]);
break;
}
});
return arrDNA;
}
console.log(pairElement("GCG"));

Free Resources

Attributions:
  1. undefined by undefined