Positive and Negative Lookahead - Regex

Positive and Negative Lookahead - Regex
0

#1

Tell us what’s happening:

Can someone explain what this code does? I tried several different expressions and finally copy-pasted the example solution and just changed the quantifier to 5.

The first lookaround appears to test for any five word characters (alphanumeric) \w{5}

The second lookaround appears to test for zero or more non-digits \D* and any digit character \d

If the first lookahead is targeting exactly 5 word characters, wouldn’t that eliminate both “bana12” and “abc123”?

Your code so far


let sampleWord = "astronaut";
let pwRegex = /(?=\w{5})(?=\D*\d)/; // Change this line
let result = pwRegex.test(sampleWord);

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/regular-expressions/positive-and-negative-lookahead


#2

check out this site https://regexr.com/ It will explain it clearly than we can in words. Open the tools bar on the bottom and select “explain”.


#3

Thanks JM! I have been using this exact site. Definitely the best site on the web for clearly teaching regex, but unfortunately I’m not able to gain any further clarity on this problem.


#4

Edited for typos

In that case, I’ll give it a shot.

First, the regex you used fails with bana12 and abc123 because it matches all the characters before. So it will continue matching indefinitely. This is because a positive lookahead means “match this, only if followed by this other thing”. But (?=\w{5}) has no preceding characters.

So for every character the regex checks the next 5 chars to see if it matches nothing. And since there’s always nothing, it will continue to match forever.

In other words, the regex reads “if nothing is followed by at least 5 characters, return nothing”. It’s not saying “exactly” as in nothing else can follow. Just that it has to be “exactly” 5 alphanumeric chars directly after.

It’s confusing to me just trying to explain it. So if you still don’t see, I could try a different explanation to see if it clicks.


#5

I’m a little confused then because when I submitted the exercise, (?=\w{5})(?=\D*\d) actually matched with bana12 and abc123.

The lookahead example on RegExr states that the positive look ahead “Matches a group after the main expression without including it in the result.”, so I agree that what I submitted shouldn’t have worked. I just used it out of curiosity because everything I’d tried up to then had failed and I know many of the FCC exercises provide examples that are close to what the solution is.

Thanks JM - I’ll continue researching this. Could it be a bug in the exercise?


#6

I should have been clearer about what failing means. I was speaking about the error message on the regex site. They warned that the expression would match infinitely. Meaning it would pass the tests, but in a real world app someone could crash you with a regex DDOS using a a few gb long string.


#7

Got it! Thanks again JM!


#8

To get a good understanding of this excercise I suggest you have a look here:

Your current solution works for the excercise because there is no test on the “consecutive”-part of the excercise. However it would still return true if the numbers were not consecutive (e.g ban1a2). So your answer works but is not really correct.


#10

I finally figured it out after checking out this topic. At first i did not understand how the given code worked:

let password = “abc123”;
let checkPass = /(?=\w{3,6})(?=\D*\d)/;
checkPass.test(password); // Returns true

But it looks like (?=\D*\d) is looking ahead to see if there are zero or more non-digits at the beginning of the string followed by a single digit. After learning this from the original poster above, i slightly modified the pwRegex to read:

let pwRegex = /(?=\w{5,})(?=\D*\d+)/g; // Change this line

What this does is in the first lookahead, the regex checks to see that there are more than 5 alphanumeric values. Then in the second lookahead, it checks to make sure that we have zero or more non-digits, followed by 2 consecutive digits with the flag g for multiples. It works just as well without the flag.


#11

Ok ! So fine ! But what about the problem statement: We need exactly two consective digits in the pattern. No g flag required as the pattern is one instance implementation. Thus follows the precise solution:

let sampleWord = “astronaut”;
let pwRegex = / (?=\w{5,}) (?=\D*\d{2}) /; // Change this line
let result = pwRegex.test(sampleWord);


#12

let pwRegex = /(?=\w{5,})(?=\D+\d)/;
The first lookaround appears to test for any five word characters (alphanumeric) \w{5,}

The second lookaround appears to test for zero or more non-digits \D* and any digit character +\d

  • It’s worked.

#13

A more rigorous regex may be as follows;

/(?=\w{5,})(?=\D*\d{2,})/

(?=\w{5,}) firstly checks to see if the string is 5 or more characters
(?=\D\d{2,}) secondly, checks to see if the pattern contains 0 or more non-numeric characters (\D*) followed by 2 or more consecutive numeric characters (d{2,})

example A: 123 would return false as it is not 5+ chars long
Example B: 12345 would return positive as it is 5+ chars long and contains at least 2 consecutive numbers (remember, the non-numeric characters were optional, so it doesn’t HAVE to contain letters)
Example C: abc12 would return positive as it is 5+ chars long and contains at least 2 consecutive numbers.


#14

This was my answer:
/(?=[A-Za-z]{3,})(?=\D*\d{2})/

After all, the expression is 5 characters long minimum, and 2 of those have to be numbers.


#15

While your code may pass the FCC tests, it does not actually meet the challenge requirements.

The following string should test true. Why? Because there are 5 or more characters AND there are 2 consecutive digits. Your code assigns false to result. Characters can be letters or numbers. Also, nothing in the instructions implies that the two consecutive digits must be at the end. They could be in the middle like the string below.

let sampleWord = "ab34ac"

#16

Thanks for the clear explanation of the solution! :ok_hand: