DNA Pairing with flexibility

Tell us what’s happening:

Anybody get through this with a more flexible solution? mine felt very hard coded, and would obviously break easily.

Your code so far

function pairElement(str) {
  var strArr = str.split("");
  var newArr = [];
  for (i = 0; i < strArr.length; i++){
    switch (strArr[i]){
      case "A":
        newArr.push(["A", "T"]);
        break;
      case "T":
        newArr.push(["T", "A"]);
        break;
      case "C":
        newArr.push(["C", "G"]);
        break;
      case "G":
        newArr.push(["G", "C"]);
        break;
    }
  }
  return newArr;
}

pairElement("GCG");

Your browser information:

Your Browser User Agent is: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/65.0.3325.181 Chrome/65.0.3325.181 Safari/537.36.

Link to the challenge:
https://www.freecodecamp.org/challenges/dna-pairing

function pairElement(str) {
  var pairs = [['A', 'T'], ['C', 'G']];
  
  var myPairs = [];
  for (i = 0; i < str.length; i++){
    for(j = 0; j < pairs.length; j++){
      var index = pairs[j].indexOf(str[i]);
      if (index >= 0) {
         myPairs.push([pairs[j][index], pairs[j][3%(index+2)]]);      
      }
    }    
  }
 return myPairs;
}

There are some reasons that this isn’t the best solution, but for whatever reason it’s the one I came up with a couple years ago. While it relies on a hardcoded array, if the the pairs were to change, the code would only need to be updated in one place and it would be easy to change it to take the pairs array as an argument.

Here’s a similar approach using the same basic logic, but much easier to read:

function pairElements(str) {
  const pairs = [['A', 'T'], ['C', 'G']];
  let output = str.split('');
  output = output.map((curr) => {
    for(let pair in pairs) {
      if(pair.contains(curr)) {
        return pair.indexOf(curr) ? pair : pair.reverse();
      }
    }
  });
}

How do I blur? Is there a forum tutorial I missed? lol

image

1 Like

Why you think it would break easily?

In my view this is the perfect place to use a map alongside a replacement table

It’s straightforward to map a function like (c) => replaceTable[c] over str.split("")

@John-freeCodeCamp here

I used a switch statement. It may not be shorter, but it’s simple and you can easily look back on it later and understand it. It’s only a couple different inputs your dealing with, so all you need is a switch statement.

There is no need to make all algorithm generic. It only needs to solve its problem domain correctly and efficiently. Furthermore, it only needs to operate well on given assumptions.

Having said that, there is nothing wrong with hard coding each symbols because that piece of knowledge is required by the algorithm.

However, if we allow more possibilities, you are right about your algorithm being too rigid. Suppose there are additional pairings for whatever reason, then you have to change the function implementation. If you are faced with 100 different pairings, you will write the code differently.

One way to decouple this is parameterizing the pairing data. For example,

const dataA = {
    A : 'T',
    T : 'A'
}

const dataB = {
    X: 'Y',
    Y: 'X'
}

// Assume only valid inputs are given
const pair = (data) => (str) =>
    Array.from(str).map( a => [ a, data[a] ]  )

const pairA = pair(dataA)
const pairB = pair(dataB)

console.log(pairA('ATAT'))
console.log(pairB('XYXY'))

(Sorry for using ES2015 if you haven’t learned it yet)

This has flexibility that allows swapping different sets of pair data with the cost of using more memory. However, if that capability is required this is good enough.

There is no error handling because no error handling has been discussed.