Positive and Negative Lookahead

Tell us what’s happening:
This code does not pass the test cases of “bana12” and “abc123” and I don’t know why…

Your code so far


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

Your browser information:

User Agent is: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36.

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

1 Like

This is really good you are working with regex too, very good.

The case study says:

Use lookaheads in the pwRegex to match passwords that are greater than 5 characters long and have two consecutive digits.

It means the word should be at-least 6 character, first note a character could be either digit or letter.

It also says it should have two digits together, and as it does not stated where?(start or end), you may assume anywhere.

First let’s start with (more than)5 character long(at-least 6, thanks @JPili for pointing out) , easy this could be .{5,}.{6,} as . means anything, and the count.

Now the two digits which is the easy part and you have already did, it should \d{2,} well done.
The one you have matches the digit at the begin. for having the digits in any place, we should tell two digits in any place.

I’m not regex expert, but I tried .*(anything, any count) before and and after the two digit pattern.

So the final pattern could be something like following
/(?=.{5,})(?=(.*\d{2,}.*))/
/(?=.{6,})(?=(.*\d{2,}.*))/
or
/(?=\w{5,})(?=(.*\d{2,}.*))/
/(?=\w{6,})(?=(.*\d{2,}.*))/
with means anything longer than 5 character, that includes two digit at any place (begin, center, end)

Keep going on great work, happy programming.

EDIT:
Thanks a million @JPili for pointing out the issue, the challenge needs more than 5 length, I got it as equal or more than 5, sorry, thanks.

22 Likes

I don’t know if you are still around, but why do you need to put the .* before and after the two digit pattern? That parts really throwing me off :stuck_out_tongue:
P.S. thanks your solution worked

3 Likes

I’m always here, and try to help people proudly :+1::muscle::sunglasses:

If you don’t place the .* (any char, any length) before the \d{2}(digit, two please), then the regex engine looks for a section to starts with two digits. This fails your tests, becasue the digic could be in any part.

Same if you remove the .* from the end, regex engine now thinks it needs to find sections ends with two digits.

When you place the .* at first and at the end, since * means 0 or any length, then it tells the regex engine to find any two digit either at start, middle or end.

Happy coding, keep going on great work and progress.

16 Likes

Thanks, this explanation cleared things up! Do all Lookaheads by default look at the first part of the string for the pattern?

2 Likes

regex engine starts looking to match all conditions of given pattern character-by-character from input value.

Conditions could be one, or multiple for each phrase(match section).

When a pattern expect two condition just like your case-study (6 length, and two digits), it filters each section by two filter, and if both returned true, match the section.

better say. if you have more than one section/match filtering (like your case-study), you should assume regex first matches the section by first filter, then for second and so on… and only returns when they all are ok.

EDIT
Fixed wrong length of 5 to correct size of 6, thanks @JPili
@UnschoolAcademy and @TheLambChop please recheck answer and edits, beside it passed the tests, but you may please considering the changes. Thanks.

2 Likes

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

Shouldn’t 5 actually be 6 since it’s inclusive of the first number and you’re only trying to match strings with more than 5 character; not strings with at least 5 characters.

That code still lets you pass the challenge but I think you should edit it to 6 so other people won’t assume that the first number isn’t inclusive because of what the challenge is asking.

2 Likes

/(?=\w{5,})(?=(.\d{2,}.))/ is wrong. It doesn’t match when two digit are placed at the end for instance, and it fails the challenge too. It also mark the very next char after two digit as end of the match, which is wrong.

Challenge says

greater than 5 characters long and have two consecutive digits

It doesn’t state place for two consecutive digits, so we assume anywhere. Also the two consecutive digits also are part of the word/match and 5 char long.

One correct regex could be /(?=.{5,})(?=(.*\d{2,}.*))/, please state the .* before \d{2,0} which means any char, zero or many, so if two digits come at the end, or start, they are accepted.

A character could be either A, a, 9, or even !m but we assumed alphabets and digits, so \w works here, otherwise it could be ., and (?=\w{5,}) ensures you the whole text is 5 long or more at the first(what challenge wants).

Oh wow I’m sorry. I somehow posted the wrong code. Weird. I tried copy and pasting the code you posted earlier but somehow posted something else. I was referring to this one:

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

It definitely passes if you change the 5 to a 6.

And regarding the challenge, I think you’re misinterpreting it. “greater than 5 characters long” doesn’t mean “at least 5 characters long.” It means “all lengths greater than 5 but not including 5.” So since the first number is considered inclusive, it should be 6 instead of 5 to follow the challenge rules.

I’m really not trying to be difficult here. I just honestly think someone will wrongly assume the first number isn’t inclusive because they think your code is following the challenge. I really think you’re doing a great job helping people out here. It’s just that one wrong piece of info.

For some reason, the challenge doesn’t fail code that match passwords that are 5 characters long. But it definitely passes code that don’t match passwords that are 5 characters long too.

2 Likes

Oh dear god, now I got it! Yes you are right, I got it as greater or equal, ye you are right, thanks for mentioning :+1::+1::metal:

I’m goin to apply the fix.

This could be great if FCC tests the regex with a 5 length string to inform user(such as me) that something is wrong.

Keep goin on great work, happy programming.

2 Likes

thanks for all those detailed explanations, helped quite a bit, I have one more question, the .\d{2,}. part is wrapped in its own brackets, do they work just like a mathematical operator, so that everything on the inside is computed first or why do you have to use them? because I noticed that the code works just fine without them…

/(?=.{6,})(?= ( .\d{2,}. ) ) /

edit nvm just found it in the next lesson :smiley:

Please note /(?=.{6,})(?= ( .\d{2,}. ) ) / is wrong, and you may considering /(?=\w{6,})(?=.*\d{2,}.*)/ as correct regex.

\d means digit [0 to 9]
{2,} means two or more, so \d{2,} means two digits(together) or more, like 00, 90, 127, 2018

. (dot) means anything
* means zero or many. So .* means anything, or nothing, like an empty string/part, or cub3fox

.*\d{2,}.* means anything or nothing at start, anything or nothing at the end, so any two digits together at start, middle or end of the part as question/assignment asked.

The first part (?=.{6,}) mean anything contains 6 any char or more, so it passes only strings more than 6 char(note char is anything)

Now /(?=\w{6,})(?=.*\d{2,}.*)/ means any at-least 6 char length, that contains at least two digits together , like cub3fox90

I hope I could explain it good, if you still have issues, feel free to reply please.

keep going on great work, happy programming.

11 Likes

Thank you very much! This makes perfect sense to me, I have no idea why so many other solutions have an extra ().

THANK YOU. Really needed this explanation.

Honestly I dont get what do you mean with “anything or nothing” . Anything means that there may be something before the numbers , in this case “bana” and “abc” right? But why nothing there? In the examples there will always be letter before the digits.
So bana12 should work with

/(?=\w{6,})(?=.\d{2,})/ig;

Note that im not using

/(?=\w{6,})(?=.*\d{2,})/ig;

Because * is for “nothing” right? but theres something behind “bana”, some letters. Why wont it work then?

Why do you need the comma after the 2? Don’t you only want two numbers?

Because there can be 2 or more numbers, so you prepare the regex expession for that.

Hi all, I’m new to this and I spent several hours thinking for the right code to use using only what I have learned so far from the previous lessons, until I found this thread.

Here’s the breakout of the requirement:

…greater than 5 characters long, do not begin with numbers, and have two
consecutive digits.

  1. Greater than 5 characters = 6 or more characters (I’m pretty sure we’re all very much aware of this)
  2. DO NOT BEGIN WITH NUMBERS
  3. Two consecutive digits (This is main discussion made here I think)

Since we’re all very much aware of the minimum characters needed to pass the passwords, we need at least 6 characters.

/(?=\w{6,})/

This is okay, until the second requirement: DO NOT START WITH A NUMBER. The code above will not satisfy this as the \w could be any letter and any NUMBER. So I just adjusted the quantifier down to {5,}. To add back the character, I use \D instead of \w, and of course before \w{5,}.

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

This will pass all of the passwords with a letter & at least 5 succeeding (any) characters, regardless of position, e. g. 1’Ab23cD’ or 56’g1234H9’. To fully pass the requirement, I used the caret (^) assertion. This is to indicate that it should be the beginning pattern.

/(?=^\D\w{5,})/

Next, is the last part of the requirement: 2 consecutive digits. Yes! The .* works like a charm. * means quantified by 0 or more times. So any character . may not exist or exist, respectively.

Positive and Negative Lookahead

Problem:
let sampleWord = “astronaut”;
let pwRegex = /change/ ; // Change the line
let result = pwRegex.test(sampleWord);

Possible Solutions:

/(?=^\D)(?=.\*\D\d{2,})/
/(?=^\D)(?=.\*\D*\d\d)/
/(?=^\D)(?=.\*\D+\d\d)/
/(?=^\D)(?=.\*\D\d\d)/
/(?=^\D)(?=.\*\D\d{2,})/
/(?=^\D\w)(?=.\*\D\d{2,})/
1 Like

Thank You so much!,I have been into this since an hour.

1 Like