Restrict Possible Usernames - what is wrong?

So i am currently on Restrict Possible usernames, and here is my code. Here is my interpretation of it:

let username = "JackOfAllTrades";
let userCheck = /[$\d|^$\d]^^\d[[a-zA-Z]{2}|\{3,}]/i;
let result = userCheck.test(username);
  • [$\d|^$\d] means it either ends in a number or doesn’t. If i just do $\d then it means it has to end in a number, which would fail the test JACK and not Oceans11

  • ^^\d means it cannot start with a number.

  • [[a-zA-Z]{2}|\{3,}] is for the 3rd condition.

  • [a-zA-Z]{2} means if its 2 characters it must be letters. Not sure if it has to be\{2} or {2}

  • \{3,} means or if its over 3 digits it can be anything.

–> https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/regular-expressions/restrict-possible-usernames
–>https://repl.it/@John_Nicole/Regular-Expression-Restrict-Possible-Usernames

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36.

Nope it doesn’t mean that. It means match with any character in [0-9_$^|].

Nope, this means start with start with a digit.

You have tendency to wrap things in [] without knowing what it does. Things in bracket expands to literal characters. Except for cases like these:
[\w], where \w expands to a certain character set,
or [^\w], where [^...] means negation of the character set.

It matches with a literal string "{3,}", the backslash escapes first disabling the quantifier. Maybe you’ve made a typo here, but maybe not.

I think you’ve built misunderstandings of Regex through trials and errors. Learning Regex by trial and error is very tiresome process. I’d suggest you to learn the actual syntax of Regex again.

1 Like

Well i am combining what i know.

^ means not, and also means start. That it why i put them together to say not start. $ means end, so i put not end.

I thought\d was [0-9]? https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/regular-expressions/match-all-numbers

I looked up length for regex and {2} or {3,} was how they did it.

Nope it doesn’t mean that. It means match with any character in [a-zA-Z0-9_$^|].

Ops, I made a mistake there, sorry for the confusion. You are right about what \d represents but just fix that part and nothing else.
So, the correct interpretation is [0-9$^|]. (I’ll edit it)

Then what you’ve wrote, [$\d|^$\d], translates to match a character within following character set:
'$' 0-9 '|' '^', '$', 0-9, which is $0-9|^.

And like I said the context matters, you put ^ in the middle character set. Character set can’t have both positive and negative match. Also, the | loses its meaning of OR inside the character set, [].

Range specifier is like what you’ve said, {2,}, {3,}, {,3} etc… But what you put in front of it is \ which treats the first bracelet literally; so, again it loses the meaning as a range specifier.

1 Like

It works. My solution
/[^\d]{2,}/i;

1 Like

You can’t write code like this. You need to be aware of context. Another example of the same type of error:

({a:1,b:2}).map(x => x * 2);

This will fail. Why? Because objects don’t have a map method. Sure, as a human, you can intuitively guess that the intention is to return {a:2,b:4}, but machines lack this type of intuition.

In exactly the same way, we (as humans) might guess your intention with ^^\d, but a machine will fail to do so. Consider some other possible meanings of ^^:

  • Not-not
  • Start-start
  • Tetration (2^^3 = 2^2^2 = 16)
  • An emoji with happy eyes
  • A literal string containing two caret characters
  • Go up, then go up again
  • Any permutation of the above options
  • … And so on

The context of ^, and by extension its meaning, depends where it’s used in the regex. If used outside of any character set (denoted by square brackets), it means the start of the string. If used at the start of a character set, it negates the set. If used elsewhere within the set, or if escaped with a backslash, it means a literal ^ character.

The same applies to many other characters, such as | and so on.

Human intuition is fragile, unreliable, and performs especially poorly in situations that require cold, hard logic (such as programming). It’s not entirely useless for this kind of purpose, but needs to be treated with a good deal of mistrust and not relied upon too heavily.

If you’re not sure how something works, Google it, read the documentation, and use great resources such as MDN. For regexes, regexr is also great, and provides a nice visual interface that gives immediate feedback.

1 Like

Sorry, but your solution doesn’t handle a handful of corner cases. For example,
!@#$!@#
11aa

These examples violates the spec #1 and #2.

  1. The only numbers in the username have to be at the end. There can be zero or more of them at the end.

  2. Username letters can be lowercase and uppercase.

If this was in a production, you will end up with a very bad situation where you either have to live with this type of exception or change the username of a user.

The only reason it might have passed the FCC test is because the test cases are weak.

1 Like

If i don’t put the \ there i get a error.

How do i put multiple | ‘or’ statements then? I thought or statements went inside braces? How do i even put one into a regular expression and put stuff after it that i don’t want being affected by it?

Then how do i? How can i make not start?

How can i do multiple or statements?

Please read the remaining 95% of my post, then Google the resources I mentioned.

1 Like

This one was trickier than I thought, probably because I suck at Regexp. After solving it, I think you will benefit more from just by looking at walkthrough rather than pulling your hair out for couple of hours. So, here we go.

First, I’ll present the working solution that also passes edge cases that FCC doesn’t check.

/^[a-zA-Z]{2,}[\d]*|[a-zA-Z][\d]{2,}$/

This consists of two parts
^[a-zA-Z]{2,}[\d]*$ and ^[a-zA-Z][\d]{2,}$
You might notice this seems totally unrelated to the exercise, but bear with me.

^[a-zA-Z]{2,}[\d]+$
This one checks:
the string beginning with at least two alphabets , ^[a-zA-Z]{2,}
followed by at least one digit towards the end, [\d]+$
So, it matches with things like

"aa"
"aaa"
"a12"
"aa123"
"aaa123"
...

Now the second part, ^[a-zA-Z][\d]{2,}$
This one checks:
the string starts with one alphabet, ^[a-zA-Z]
followed by at least two digits towards the end, [\d]{2,}$
It matches with things like:

"a12"
"a123"
...

If a username matches with either of the condition, it conforms to the given constraints.

  1. The only numbers in the username have to be at the end. There can be zero or more of them at the end.

  2. Username letters can be lowercase and uppercase.

  3. Usernames have to be at least two characters long. A two-letter username can only use alphabet letter characters.

Now, the whole reason I did this is because of the constraint #3, “Two letter username can only use alphabets”

The first two cases are straight forward:
^[a-zA-Z]+[\d]*$
At least one alphabet followed by optional digits. This also conforms to the first half of the constraint #3, minimum length = 2.

But as soon as you add the second half of the constraint #3, things get really messy.
You might think you can just use alternate match with | with [a-zA-Z]{2}
Like ^[a-zA-Z]+[\d]*|[a-zA-Z]{2}$
We just check for the special case where username is length 2, easy right? nope.
This gives false positive for things like "a1", which clearly violates constraint#3.

Now this “a1” becomes the new special case, that we should avoid. The root of problem is our first Regex
^[a-zA-Z]+[\d]*$
One obvious way to avoid our new special case is
^[a-zA-Z]{2,}[\d]*$
Now, "a1" fails, which is good, but we can’t have things like "a12".
Let’s fix that by adding alternative case ^[a-zA-Z][\d]{2,}$
We can concatenate alternative pattern with |

Finally, we arrive at

/^[a-zA-Z]{2,}[\d]*|[a-zA-Z][\d]{2,}$/
2 Likes

Wow regex is confusing.

The challenges only teach you what each thing means, in no way does it show you how to put them together correctly.

But how? I thought it matched not made it optional.

What does the +$ mean that seems to be random.

Where did you learn regex?

There’s another typo there it should be [\d]+$ :frowning: sorry for the confusion. (I was editing back and forth)

So, [\d] matches any single digit. + further specifies quantity, and $ checks if previous pattern is true at the end. Together, they read as match a sequence that ends with at least one digit. In general, anything inside [] is treated as a literal character and anything else have speical meaning depending on some context.

I learned Regex from MDN and a couple of chapters from the book “Mastering Regular Expression”. This source is good as well, http://www.grymoire.com/Unix/Regular.html

I don’t really think the exercise was fair for someone just learning Regex.

1 Like

So i had my crack at some Coding train videos and i am trying to do this problem for myself, so i can learn.

First i am just trying to match anything that has 3 or more letters characters, and has a optional ending of a number.

Here is my code: /\w{3,}\d?/i

\w\s means one character means one letter one space? https://youtu.be/YTocEnDsMNw?t=294

Using that i tried to say 3 or more \w with a optional \d

Now its failing to check for letters. Ja1 works, but so does 1ac.

So i do /\[a-zA-Z]{2,}\d+?/i so i can have two letter characters and a optional number(s) ?

Now everything seems to be false → https://repl.it/@John_Nicole/Regular-Expression-Restrict-Possible-Usernames

Okay so i removed the slash and it works with numbers. I tried using the question mark to make it optional from all the documentation i read but it isn’t working.

I used the idea of \w\s meaning one letter one space, so i said 2 or more letters with a 0 or more numbers.

In general, \<lowercase_char> expands to a predefined character set. In this case, \w expands to [a-zA-Z0-9_].
As a side note, \<uppercase_char> often expands to the complement, \W := [^a-zA-Z0-9_]. Notice [^...] this form is negation.
In any other cases, \ usually escapes special symbol to literal. e.g) \[] If you do this, the [] no longer means character set. Because [ is treated literally, like '['].

If you want just the alphabets, [a-zA-Z] is the way to go, or [a-z] with i modifier.

Assuming you already know range specifier and quantifier, +3 alphabets with optional 1 digit would be.
/[a-z]{3,}\d?/i

1 Like

So this is my code now:

/[a-zA-Z]{2,}\d*?/i

How come it still passes a string with a number at the front?

@gunhoo93

Again, you do not need the A-Z because you are using the i flag at the end of the regular expression.

Hint: To solve your dilemma, think about a special character you learned about which defines the beginning. Also, do you really need both the * and the ?

1 Like

I got it :smiley:

The ^ was what i needed, and a $ at the end.

/^[a-z]{2,}\d*?$/i