This one took me a while to figure out and while I do have an answer I’m not sure it’s the most efficient but wanted to share for anyone who needs support.
As a quick note this answer does use {} which occur in the lessons after this problem, so to give a quick explanation, {} have 3 uses (The following explanations use “m” and “n” as variables that represent numbers) It can be used as {m, n} where the preceding element or subexpression must occur between m and n times, inclusive: ie [a-z]{2,4} will search for a string with lowercase letters that is 2 to 4 characters long. It can be used as {m} where the preceding element or subexpression must occur exactly m times: ie [a-z]{2} will search for a string with lowercase letters that is exactly 2 characters long. The final use is {m, } where the preceding element or subexpression must occur at least m times: ie [a-z]{2,} will search for a string with 2 or more lowercase letters. I will be using this last version {m,} for my answer. This article on oreilly explains it more in-depth if you need a deeper dive.
As a way to follow along here are the 4 requirements again:
-
Usernames can only use alpha-numeric characters.
-
The only numbers in the username have to be at the end. There can be zero or more of them at the end. Username cannot start with the number.
-
Username letters can be lowercase and uppercase.
-
Usernames have to be at least two characters long. A two-character username can only use alphabet letters as characters.
I focused on number 2 first since the ^ and $ symbols allow you to control how things start and end respectively and so controlling that felt pretty easy. I used the [a-z] to negate numbers at the beginning and \d to allow for numbers at the end. I also used * in addition to my \d to allow for zero or more of numbers at the end. That got me:
/^[a-z]\d*$/i
Now I did initially use negate numbers ie /^\D...
but I realized this allows for special symbols like _^%* etc. where as /^[a-z]
placed with the i at the end restricts the answer to only letters and allows for lower and uppercase letters which helps uphold requirements 1 and 3.
Also looking at requirement 1, it can slightly trip you up since it says the username can be letters and numbers, however since your numbers can only be at the end (requirement 2), it means everything in the beginning and middle have to be letters. We have already solved for numbers at the end with \d*$, and strings at the beginning with ^[a-z] so we just want to capture everything in the middle and I thought of doing this in 2 ways (of which there are many) and had these two options along the way:
/^[a-z]+\d*$/i
or
/^[a-z]\d*$/gi
The + makes sure you start with a letter and then adds for more letters in the middle until you reach the numbers at the end. The g makes your regex greedy which will have [a-z] implicitly search for more letters beyond just the beginning string.
With 3 out of 4 requirements covered the 4th requirement is where things can diverge. What’s tricky about requirement 4 is that if your username is 2 characters it has to be alphabet letters: ie “Jo” and not “J7” but if it is greater than 2 characters it can only have 1 alphabet letter: ie “J79”. In order to test for both of those scenarios you have to use the pipe | syntax.
Now before I present the answer that uses {m,}, I will first explain sikaili99’s answer above which is a great use of the things that have been taught in the FCC lessons before this challenge.
It starts off like our tests so far /^[a-z]
, which evaluates to begin with a letter, then it bundles the two tests to make requirement 4 work in parenthesis: ([a-z]+\d*|[\d][\d])
. To expand it out, the first part is /^[a-z][a-z]+\d*
in other words begin with a letter /^[a-z]
, make sure it’s followed by 1 or more letters [a-z]+
, and you can end with zero or more numbers \d*
. This allows for matches with “jo”, “jo7”, "joo7 “jo76” and not matching with “j” or “j7”. The second part expands out to /^[a-z][\d][\d]
, so it still starts with a letter /^[a-z]
, but in this case it follows the letter with a number [\d]
, and requires that to also be followed with a number [\d]
, so that you capture valid matches like “k79” which would be rejected by /^[a-z][a-z]+\d*
because it does not start with two letters. After the parenthesis is $/i
, the $ ensures the final check in the parenthesis happens at the end (ie the number \d) and the i allows for upper case letters. The only change I would add to this solution is making the second check look beyond 2 numbers “...|[\d][\d]+)$/i
” since this will also capture “k796” which just looking for two numbers ([\d][\d]
) will not.
Now for the solution that uses {m,}. It also uses the pipe but uses {m,} to specify the least amount of numbers or letters needed. It looks like this: /^[a-z]{2,}\d*$|^[a-z]\d{2,}$/i
. The first part before the pipe evaluates to, start with 2 or more letters ^[a-z]{2,}
and end with zero or more numbers \d*$
which handles “jo”, “jo7”, "joo7 “jo76” and not matching with “j” or “j7” and after the pipe allows for matches like “A79” by also starting with a letter ^[a-z]
but then checking if we end with 2 or more numbers \d{2,}$
to assure the username is 3 or more letters (1 letter and 2 or more numbers) and then ending with a /i to allow for uppercase letters.
This is very long, but I hope this helps with some of the thinking.
I would also like to note to the people setting unit tests that there is currently no tests for symbols like &%$* in the problem.