freeCodeCamp Algorithm Challenge Guide: Validate US Telephone Numbers

freeCodeCamp Algorithm Challenge Guide: Validate US Telephone Numbers
0

#1

:triangular_flag_on_post: Remember to use Read-Search-Ask if you get stuck. Try to pair program :busts_in_silhouette: and write your own code :pencil:

:checkered_flag: Problem Explanation:

The task is not that hard to understand, implementing it is the hardest part. You have a to validate a US phone number. This means there is a certain amount of numbers required, while you don’t need to put the country code, you will still need the area code and use one of the few formats allowed.

Relevant Links

:speech_balloon: Hint: 1

There is no way around it, you will need to brush up your regular expressions skills.

try to solve the problem now

:speech_balloon: Hint: 2

Try using a site from the previous list to test the regex live while you create it.

try to solve the problem now

:speech_balloon: Hint: 3

Start by trying to get it to validate each format from the example, each one should take a new line, once you get to select them all, then add examples that should not be selected and make sure they are not selected.

try to solve the problem now

Spoiler Alert!

687474703a2f2f7777772e796f75726472756d2e636f6d2f796f75726472756d2f696d616765732f323030372f31302f31302f7265645f7761726e696e675f7369676e5f322e676966.gif

Solution ahead!

:beginner: Basic Code Solution:

function telephoneCheck(str) {
   var regex = /^(1\s?)?(\(\d{3}\)|\d{3})[\s\-]?\d{3}[\s\-]?\d{4}$/;
   return regex.test(str);
}
telephoneCheck("555-555-5555");

:rocket: Run Code

Code Explanation:

  • ^ denotes the beginning of the string (1\s?)? checks allows for a “1” or a "1 " at the beginning.
  • \d{n} checks for exactly n number of digits so (\(\d{3}\)|\d{3}) checks for three digits that are allowed to be between parenthesis.
  • [\s\-]? checks for spaces or dashes between the groups of digits.
  • $ denotes the end of the string. In this case the beginning and end of the string are used in the regex to prevent it from matching any longer string that might contain a valid phone number (eg. “s 555 555 5555 a”).
  • Lastly we use regex.test(str) to test if the string adheres to the regular expression and return true or false.

Relevant Links

:sunflower: Intermediate Code Solution:

function telephoneCheck(str) {
  var re = /^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})$/;
  return re.test(str);
}
telephoneCheck("555-555-5555");

:rocket: Run Code

Code Explanation:

This is an example of a very comprehensive and robust solution to validating US phone numbers client side. In such cases it might be much better and easier to implement this library libphonenumber.

Relevant Links

:clipboard: NOTES FOR CONTRIBUTIONS:

  • :warning: DO NOT add solutions that are similar to any existing solutions. If you think it is similar but better, then try to merge (or replace) the existing similar solution.
  • Add an explanation of your solution.
  • Categorize the solution in one of the following categories — Basic, Intermediate and Advanced. :traffic_light:
  • Please add your username only if you have added any relevant main contents. (:warning: DO NOT remove any existing usernames)

See :point_right: Wiki Challenge Solution Template for reference.


#2

#3

#4

anyone wanna chime in on the use of REGEX for validation of phone numbers?


#5

Yeah… I didn’t like the idea of using RegEx for the whole thing, for exactly the same reason as stated in the stackoverflow link @EgoDominusVos shared (which I’m sure is what led to the Intermediate solution):

don’t use a regular expression to validate complex real-world data like phone numbers or URLs. Use a specialized library

… so, I took a pedantic approach that I suppose shouldn’t be counted as an “algorithm”


function telephoneCheck(str) {
  var len = function(item) { return item.length; };
  var nxxVal = function(nxx) { return nxx[0] > 1 && nxx[1] + nxx[2] != 11; };
  var npaExchVal = function(item) {
    return nxxVal(item.slice(-10, -7)) && item[len(item) - 9] != 9 && nxxVal(item.slice(-7, -4));
  };
  var strNums = str.replace(/[^\d]/g, "");
  var strForm = str.trim().replace(/[^\d\s\(\)\-]/g, "_").split("").reverse().join("");
  
  function numsVal() {
    if (len(strNums) < 10 || len(strNums) > 11) { return false; }
    if (len(strNums) === 10) { return npaExchVal(strNums); }
    else if (len(strNums) === 11) { return npaExchVal(strNums) && strNums[0] == 1; }
  }
  function formVal() {
    if (/_/.test(strForm)) { return false; }
    if (/[^\d]/.test(strForm.slice(0, 4))) { return false; }
    if (/-/.test(strForm[len(strForm) -1])) { return false; }
    if (/[\s-][\s-]/.test(strForm) || /-[\(\)]/.test(strForm) || /[\(\)]-/.test(strForm) || /[\(\)][\(\)]/.test(strForm)) { return false; }
    if (/[\(\)]/.test(strForm)) {
      if (!/\(/.test(strForm) || !/\)/.test(strForm)) { return false; }
      if (strForm.search(/\)/) < 7 || strForm.search(/\)/) > 9) { return false; }
      if (strForm.search(/\(/) - strForm.search(/\)/) !== 4) { return false; }
      if (strForm.indexOf("(") !== strForm.lastIndexOf("(") || strForm.indexOf(")") !== strForm.lastIndexOf(")")) { return false; }
      if (/[^\d]/.test(strForm.slice(strForm.search(/\)/) + 1, strForm.search(/\(/)))) { return false; }
    }
    if (/[\s-]/.test(strForm)) {
      if (strForm.search(/[-\s]/) < 4) { return false; }
      if (/\s/.test(strForm[4]) && /-/.test(strForm)) { return false; }
      if (/[\s-]/.test(strForm[4])) {
        if (/[^\d]/.test(strForm.slice(5, 8)) || /\d/.test(strForm[8])) { return false; }
        if (/\d/.test(strForm[9])) {
          if (/[^\d]/.test(strForm.slice(9, 12)) || /\d/.test(strForm[12])) { return false; }
        }
      }
      else if (/\d/.test(strForm[7])) {
        if (/[^\d]/.test(strForm(7, 10))) { return false; }
      }
      else if (/\d/.test(strForm[8])) {
        if (/[^\d]/.test(strForm(8, 11)) || /\d/.test(strForm[11])) { return false; }
      }
    }
    return true;
  }
  return numsVal() && formVal();
}


#6

I just passed this challenge, this is what I came up with, I see that it’s recommended to use a single regexp but I couldn’t wrap my head around it to do that, constructive feedback appreciated:

function telephoneCheck(str) {
    // Good luck!
    var countDash = 0;
    var countPar = 0;
    str = str.replace(/\s*/g, '');
    if (str.search(/\W*\D*[^-()]*/g)) {
        return false;
    }
    str = str.split('');
    for (var i = 0; i < str.length; i++) {
        if (str[i].search(/[()]/) != -1) countPar++;
        if (str[i].search(/[\-]/) != -1) countDash++;
    }
    if (((countDash !== 2 && countDash !== 1) && countDash > 0 ) || (countPar !== 2 && countPar > 0)) {
        return false;
    } else if (countPar > 0 && str[str.indexOf('(') + 4] != ')') {
        return false;
    }
    console.log(str);    
    return (str.length - (countDash + countPar) == 11 && str[0] == 1) || str.length - (countDash + countPar) == 10;
}

#7

Hey,
I stuck on one point - which is the begining of the RegExp. I had resort to check the solution as I couldn’t cope with it. Could any one explain me how this phrase work: /^(1\s?)?((\d{3})|\d{3}) - this beginning part combination of ^(group)? ?

I understand single used metacharacters as I also used them. What I do not understand is why this return false for phrase which begins with eg. 2 (anything else than 1), and true when string lacks such group,
(of one character and space), begins with for example 555, which is definitely not 1. I see it (wrongly) this way that question mark makes “match either zero or one of the preceding character or group”. It expresses optionality. Character '2(space) ’ doesn’t fit obviously, so it would match zero of the preceding group. As for me the same goes when there’s no set of characters that that would fit that group . Why there’s a difference when there;'s more characters? I would think that it should just ignore any text that doesn’t fit (like aforemention “2 space”), and go further trying to make a match.
Thanks in advance.


#8

Zap -

I too am still working to understand Regular Expressions, so I am not absolutely sure that what I am telling you is 100% correct, but I will try to explain my interpretation of this expression. I hope it helps you.

According to MDN RegExp :

-Special characters meaning in regular expressions-

-Tables section: Quantifers

_x? - matches the preceding item x, 0 or 1 time.

The first question mark in the expression refers to the “\s” or single white space that may be matched 1 or 0 time. The second question mark refers to the entire group within the parenthesis"(1\s?)". This entire expression may be matched 1 or 0 times. So the one with or without a single space may be matched one or zero times.

The phrase ‘matched zero times’ is applies to your question. If the first character is not a one or a one with a single space, then test moves to the next portion of the expression. The next expression evaluates for a group of characters which must be a three digit group, either within or without a beginning and ending parenthesis. ((\d{3})|\d{3})

Table section : Alternation

x|y - matches either x or y

In our case, (\d{3}) or \d{3}. The first refers to three digit within parenthesis and the second refers to a three digit group with out parenthesis.

The next portion used the question mark again to indicate there may or may not be a single white space or dash.

The final portion evaluates for a four digit group.

After all that we move to the second line of code.
This line returns the result of the Method .test() of this regular expressions

  • RegExp.prototype,test().

I hope this helps.


#9

Thanks a lot for your insight. Hope it help sb in need. It took some hours before it has dawned on me, how it works. Guess you to digest the rules. Sleep over it. Looking back, this one was one of the hardest exercise on FCC for algorithms. So sweat and cryies are common :slight_smile: Thanks again.
GZ


#10

Not the best solution, but it works. Took a little bit longer approach reading up on sorting U.S phone numbers as opposed to using the six criteria given in the description.

function telephoneCheck(str) {
  
 var regex = /^(?:(?:\+?1\s*(?:[-]\s*)?)?(?:\(\s*([2-9][02-8][02-9])\s*\)|([2-9][02-8][02-9]))\s*(?:[-]\s*)?)?([2-9][02-9]{2})\s*(?:[-]\s*)?([0-9]{4})?$/gm;
  
   if(str.length < 10){
    return false;
  }
  
 return regex.test(str);
}
telephoneCheck("555-5555");

#11

also not the best but it works!

function telephoneCheck(str) {
  // Good luck!
  var ck = -1;
  
  
  str = str.replace(/\s/g, '');
  
  var ar = str.match(/^1?(\()?\d{3}(\))?[\-|\s]?\d{3}[\-|\s]?\d{4}/);
  
  if (Array.isArray(ar)){
  
  if(Array.isArray(ar) && (ar[1] !== undefined && ar[2] !== undefined)){
    ck = 0;
     }
  else if(Array.isArray(ar) && (ar[1] === undefined && ar[2] === undefined)){
    ck = 0;
  } }
 
  
  return ck===0 && ar[0].length == str.length ? true : false;
}



telephoneCheck("555-555-5555");

#12

Here’s the mental algorithm I used to solve this independently. Hopefully it’ll be helpful.

  1. If you’re new to regular expressions you need to use a live RegEx editor to visualize the impact of your edits in real time. It’s an invaluable learning tool. I used RegExr.
  2. Test all the sample answers and requirements/conditions from the FCC challenge page with the RegEx editor.
  3. Use brackets around each element of your regular expression. For me, it really helped visually tidy up the code and I could read and understand it much better.
  4. Group the sample answers and requirements/conditions by patterns to help you identify what they have in common and how to detect them.
  5. While testing out variations of your regular expression, categorize your results in the following 4 lists: (1) Correct True (2) Incorrect True (3) Correct False (4) Incorrect False. This way, you can see if you’re making progress. If your next attempt gives you less false positives, you’re on the right track! If it gives you more false negatives, your previous attempt was better.

~SPOILER ALERT!!~~~~~~

Lastly, here’s my solution. It’s not the most elegant, but I’m pretty satisfied with it and the process I took to arrive at it. RegExr was an awesome tool!

function telephoneCheck(str) {
   return /^1?( )?((\(\d{3}\))|( \d{3} )|(\d{3}))( |\-)?(\d{3})( |-)?(\d{4}$)/.test(str);  
}

#13

It was difficult though:

function telephoneCheck(str) {  
  var regEx = /^(1?\s?)?(\(\d{3}\)|\d{3})[\s\-]?(\d{3})[\s\-]?(\d{4})$/gm;
  var flag = regEx.test(str); 
  return flag;
}



#14

You can kind of see me learning how to use regEx as I worked through the solution… I started trying to do it algorithmically and did the rest with a regex… will edit to use all regexp but saving here for posterity:

function telephoneCheck(str) {
  
  if (str[0] === "1") {
    str = str.slice(1, str.length);
  } if (str[0] === " ") {
    str = str.slice(1, str.length);
  }
  if (str.match(/\d/g).length !== 10) {
    return false;
  } else if (/^([(]\d{3}[)]|\d{3})[ -]{0,1}\d{3}[ -]{0,1}\d{4}/.test(str)) {

      return true;
  } else {
    return false;
  }
}



telephoneCheck("1 555-555-5555");

#15

This took me hours, but I sure learned a lot more about regex in the process. Even though it would pass the test without it, I decided to be sure the area code and prefix never started with zero.

Just in case anyone happens to be using RegExr.com to test the list of phone number patterns, make sure to click on the closing flags and check multiline and global.

function telephoneCheck(str) {

  var re = /^(1[\s-]?)?(\([1-9]\d\d\)|[1-9]\d\d)[\s-]?[1-9]\d\d[\s-]?\d{4}$/g;
  return re.test(str);
}

#16

Hey, if you’re still around, i’m curious what it is about the code you posted that disqualifies it as an algorithm? I’m hoping to learn a better definition so I can better approach these problems.


#17

Thank you so much for this explanation! I completed the challenge in a much different way before opening up this hints section. I used str.replace, for and if statements to finish it off. Once I opened up the hints and read the Basic Code Solution, my head almost exploded trying to dissect it. Your explanation was so crystal clear that I know feel much more comfortable using regex moving forward.

I’m going to comment out my original code for the challenge but leave it there so I can see how I used to do things like this. I’m going to add in the basic solution and a direct link to your explanation so that I know how this works and where I learned it. I also took an entire page of notes on this answer using your explanation as well.

I cannot thank you enough for helping us campers with this challenge!


#18

Hi,
I have a very similar solution to you
function telephoneCheck(str) {
// Good luck!

return /^[1]?([\s-])?((([\d]{3}))|([\d]{3}))([\s-])?([\d]{3})([\s-])?([\d]{4}){1}$/.test(str);
}


#19

Hi y’all, I have a solution, similar to the basic one.

function telephoneCheck(str) {
   return (/^1? ?(( ?\d{3}[- ]*)|(\( ?\d{3}[- ]*\) *))\d{3}[- ]?\d{4}$/).test(str);
}

I actually was able to do it pretty quickly, which I attribute to the fact that I spent some time today watching this great Regular Expressions YouTube playlist by codecourse. I had tried to learn regular expressions before, but struggled immensely. Watching this helped so much! And the whole thing is less than 30 minutes.

I also used RegExr to practice and build out my solution. If you’re not using something like that, definitely check it out. It will make your life a million times easier.


#20