3
\$\begingroup\$

Here is another challenge from Coderbyte. I found this one challenging, although not quite as much as the previous two I've posted. Based on the feedback I received on my earlier posts, I structured this solution as a function. It is probably better than my previous solutions but still could be improved.

In particular, I am wondering if there is a way to condense my while loops, perhaps by determining the a and b indices at the same time or maybe by building a helper function? It seems like there is redundancy there. Resetting the index variable strikes me as particular clumsy or inelegant, but it had to happen somehow. I'm also not to happy with splitting the string and then joining the modified array to deal with the spaces. Any feedback on these issues or other suggestions to improve the code would be greatly appreciated.

Here's the challenge, slightly modified:

Take a str parameter and return true if there are any occurrences of characters "a" and "b" separated by exactly 3 places (i.e. "lane borrowed" would result in true because there is exactly three characters between a and b). Otherwise return the string false.

(While I was posting this, I realized I had not accounted for spaces. My interpretation of the instructions is that spaces do not count as characters. I have updated my code accordingly.)

var str = prompt("Please enter a string: ").split("");

function test(str){ 
var index = 0;
var arrayA = [];
var arrayB = [];

while (index >= 0){ //removes spaces
  index = str.indexOf(" ");
  if (index >= 0) {
  str.splice(index, 1);
  }
}
str=str.join("");
index = 0;

while(str.indexOf("a",index) != -1){ //Identifies indices of "a"
  arrayA.push(str.indexOf("a",index));
  index = str.indexOf("a", index)+1;
}

index=0;

while(str.indexOf("b",index) != -1){//Identifies indicies of "b"
  arrayB.push(str.indexOf("b",index));
  index = str.indexOf("b", index)+1; 
  }

for(var i=0; i<arrayA.length; i++){//determines if any a's and b's are 3 characters apart
  for(var j=0; j<arrayB.length; j++){
    if (Math.abs(arrayA[i]-arrayB[j]) === 3){
      return true;
    }
  }
}
return false;
}

console.log(test(str));

Here's a revised version:

function ABCheck(str) { 
  str = str.split("");
  for (var i = 0; i<str.length; i++){
    if (str[i] === " "){
      str.splice(i,1);
      console.log(str);
    }
  }
  for (var i = 0; i< str.length; i++){
    if (str[i] === "a" && (str[i-3] === "b" || str[i+3] === "b")){
      return true; 
    }
  }
  return false; 
}
\$\endgroup\$

3 Answers 3

3
\$\begingroup\$

I would recommend using a regular expression. Regex is designed for solving pattern matching and string manipulation problems. Although it can look esoteric and terse it is well worth the effort to learn since it is very powerful and fairly ubiquitous. It is very likely your programming language/shell/editor of choice supports a flavor of regex.

var str = prompt("Please enter a string: ");

function test(str){ 
  return /a\w{3}b/.test(str) || /b\w{3}a/.test(str); 
}

console.log(test(str));
\$\endgroup\$
5
  • \$\begingroup\$ Correct me if I'm wrong but I think that will fail on b123a? \$\endgroup\$
    – ChrisWue
    Oct 16, 2013 at 20:31
  • \$\begingroup\$ Common misconception. The character class \w includes both A..Z, a..z, 0..9, and the _ underscore character. \$\endgroup\$ Oct 16, 2013 at 20:36
  • \$\begingroup\$ I didn't mean the character class. You check for an a follow by the characters followed by a b. \$\endgroup\$
    – ChrisWue
    Oct 16, 2013 at 20:44
  • \$\begingroup\$ Apologies, misread your question. Corrected my response. \$\endgroup\$ Oct 16, 2013 at 20:49
  • \$\begingroup\$ Thanks, regex seems to be the correct approach for a number of the challenges I have attempted. I am reading up on it. \$\endgroup\$
    – Nathan
    Oct 21, 2013 at 4:03
1
\$\begingroup\$

I would also use a regular expression match, similar to @ScottPuleo's approach. Regular expressions are just so perfectly suited for this kind of problem!

The difference is that I would construct the regular expression to cover both cases (either a followed by b, or b followed by a). I've interpreted "three places" in your problem to mean any three characters other than newline characters. (That is, a and b are also acceptable as intervening characters.)

var str = prompt("Please enter a string: ");
console.log(/a...b|b...a/.test(str));

I didn't bother with putting the code in a function this time, since it's just one line with no side effects.

\$\endgroup\$
1
\$\begingroup\$
  1. Your trimming of whitespaces is not the most efficient - you only remove one at a time. replace with a regular expression should do the trick: str = str.replace(/\s+/g, "");
  2. If you rephrase the problem slightly you will find that a more efficient solution comes to mind: Any occurrence of a and b separated by exactly three characters means "Is there an a three characters on either side of any b or a b three characters on either side of an a. The algorithm is:

    • For every letter in str:
      • if current letter is an a check for a b 3 characters to left and to the right
      • else if current letter is an b check for a a 3 characters to left and to the right

    This loops over the string once and you don't need to collect all the indices. charAt is your friend when implementing this.

\$\endgroup\$
2
  • \$\begingroup\$ I added a revised solution using some of your suggestions. I am using the loop to splice interior white space. I don't think trim will do that but will remember it for the future. I have used in java but not javascript. Thanks. \$\endgroup\$
    – Nathan
    Oct 21, 2013 at 4:04
  • \$\begingroup\$ @Nathan: True, I misread the code. \$\endgroup\$
    – ChrisWue
    Oct 21, 2013 at 7:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.