1
\$\begingroup\$

Here is a slightly modified challenge from Coderbyte:

Determine if a given string is an acceptable. The str parameter will be composed of + and = symbols with several letters between them (ie. ++d+===+c++==a) and for the string to be true each letter must be surrounded by a + symbol. So the string to the left would be false. The string will not be empty and will have at least one letter.

For whatever reason, I have struggled mightily with this challenge. I tried to use a regular expression in my Booleans, I tried cascading if statements, I tried a while statement surrounding if statements, I tried to structure my code as a function. After hours of failure, I finally resorted to the following:

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

if (((str[0] != "+") && (str[0] != "=")) || ((str[str.length-1] != "+") && (str[str.length-1] != "="))){
  answer = "false";
}

for (var i = 1; i < str.length-1; i++){
  if (((str[i] != "+") && (str[i] != "=")) && (str [i-1] != "+" || str [i+1] != "+")){
      answer = "false";
  }
}  

console.log(answer);

I think it works, but of all the possible solutions to this problem, I am guessing that it is toward the bottom in terms of efficiency and elegance. I'd really appreciate some guidance on how this could be improved.

Below is one of my "solutions" that did not work. Among other things, the Booleans are not evaluating correctly. I must be using the regular expressions incorrectly.

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

if ((str[0] === /[a-z]/gi) || (str[str.length-1] === /[a-z]/gi)){
    answer = "false";
}
for (var i = 1; i < str.length-1; i++){
  if (str[i] === /[a-z]/gi && (str [i-1] != "+" || str [i+1] != "+")){
      answer = "false";
  }

}  

console.log(answer);
\$\endgroup\$
2
  • \$\begingroup\$ The problem statement is not precise. "Each letter must be surrounded by a + symbol" — does that mean immediately surrounded, or would +ab+ be acceptable? Is it acceptable for + symbols to be shared, like +a+b+? \$\endgroup\$ Oct 15, 2013 at 23:54
  • \$\begingroup\$ This is pretty much verbatim from Coderbyte. My interpretation of the problem statement is that the '+' signs must immediately surround the alphabetical character so '+ab+ is not acceptable; however, there was no prohibition against sharing a '+' so '+a+b+' would be acceptable. \$\endgroup\$
    – Nathan
    Oct 15, 2013 at 23:59

2 Answers 2

2
\$\begingroup\$

Your version is actually not that bad but could be simplified slightly and wrapped in a function. You could use charAt to get the character from a string instead of splitting the string. You don't need the initial check on the first and last characters; you can just do these as part of the main loop. (This works because str.charAt(-1) and str.charAt(str.length) both just return empty strings. Similarly if you were using an array, arr[-1] returns undefined.)

Wrapping this in a function and returning false when a non-matching character is found makes the code more efficient, as it does not have to run through the remaining loops when it already knows the answer (this would be the same as inserting break into your code.)

The below code also checks that the characters are either +, =, or a letter, which your original code does not do (so it would accept "===+!+==+3+", for example).

var str = "++d+===+c++=a";
console.log(checkString(str));

function checkString(str) {
    for (var i = 0; i < str.length; i++) {
        if (str.charAt(i) != '+' && str.charAt(i) != '=' && 
            !(str.charAt(i) >= 'a' && str.charAt(i) <= 'z' && 
              str.charAt(i - 1) == '+' && str.charAt(i + 1) == '+')) {
            return false;
        }
    }
    return true;
}

To use regular expressions you need to write pattern.test(str), e.g. /[a-z]/gi.test('a') returns true. However the point of using regex is that you can test a whole string in one go, rather than character-by-character. I think the following will work:

/^((\+[a-z])*\+|=)+$/gi.test('++d+===+c++=a')
\$\endgroup\$
2
  • \$\begingroup\$ This looks great. When you write short scripts like this, how do you test them? I am using the Firebug console. Also what does "if (str.charAt(i) != '+' && str.charAt(i) != '='" accomplish that is not accomplished by "!(str.charAt(i) >= 'a' && str.charAt(i) <= 'z'?" Thanks for your help. Very nice! \$\endgroup\$
    – Nathan
    Oct 15, 2013 at 19:10
  • \$\begingroup\$ Test with jsfiddle.net. You still have to open the browser's console to see console.log output. The condition checks (i) character is not + (ii) character is not = and (iii) character is not a letter, or if it is, it is not surrounded by +s. If any of these negative conditions are true it returns false. \$\endgroup\$
    – Stuart
    Oct 15, 2013 at 19:29
1
\$\begingroup\$

The simplest solution is to use a regular expression, and nothing but a regular expression.

function checkString(str) {
    return /^(?:\+[a-z](?=\+)|[+=])+$/i.test(str);
}

Explanation, starting from the outside going inward:

  • ^(?:something)+$ — The string must consist solely of one or more repetitions of something. (We could use * instead of +, but the problem states that str is non-empty, and using + enforces that assertion.)
  • something|[+=] — We are looking for something. However, + and = characters may also appear anywhere and that's OK.
  • \+[a-z](?=\+) — A + sign, followed by a letter (case-insensitive due to the //i flag), followed by another +. The backslashes are necessary because + by itself would mean "one or more repetitions of something". The second + is inside a positive look-ahead assertion, so that it does not get consumed when this snippet matches, which allows the + to be shared between two letters as in +a+b+.
\$\endgroup\$
4
  • \$\begingroup\$ Okay, that's pretty amazing. I just encountered regular expression for the first time about two weeks ago so they are still pretty mysterious to me. To the uninitiated, they almost look randomly generated. Your explanation is very helpful in this case. Can you back up one step and explain how the regular expression returns a Boolean(?) value? It seems like the regular expression represents both a logical comparison and one of the terms of the comparison. Also if you have any recommendations for good materials on regular expressions, I would appreciate them. Thanks \$\endgroup\$
    – Nathan
    Oct 16, 2013 at 2:09
  • \$\begingroup\$ A language feature just for solving your problem — how neat is that? =) The basic ideas are simple, but some some patterns are hard to formulate regular expressions for. Also, each programming language has a slightly different flavour of regular expressions, with varying feature sets. Here's an introductory website. Also read the docs for the JavaScript RegExp object. A good comprehensive book is Mastering Regular Expressions by J. Friedl. \$\endgroup\$ Oct 16, 2013 at 6:05
  • \$\begingroup\$ The regular expression just describes the pattern you are looking for. The RegExp.test(str) function returns true or false depending on whether the string fits the pattern. \$\endgroup\$ Oct 16, 2013 at 6:08
  • \$\begingroup\$ I see now. Funny, it all looked like random characters to me, including the function, but slowly it is starting to resolve into more meaningful units. Thanks for the resources. \$\endgroup\$
    – Nathan
    Oct 16, 2013 at 16:01

Your Answer

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

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