In the challenge Positive and Negative Lookahead, the tests are broken and so is the solution:
The challenge requires for the first character to not be a number, at the moment,
My best suggestion is to remove the requirement for the first character to not be a number
To add some context to @thecodingaviator's issue. None of the following should match with a valid solution, so we should definitely have some tests look for a few of these. The current tests and solution added to challenge file match all of these incorrectly according to the instructions.
1baz22w
36b3ez
395ezb
2783ez
36838z
783869
Before creating a PR for this issue, please wait until we have mapped out all the other edge cases the current tests and solution which are currently being passed or failing incorrectly with the current file on the master
branch. Once we have a consensus, then we will gladly accept PRs.
Thank you.
@thecodingaviator I came up with the following for the solution, which seems to correctly pass the existing tests and would correctly not match any of the above 6 examples.
I think as far as extra tests go, we could either pick 2-3 of the ones above ones which should not match and be good enough.
let pwRegex = /^[a-z](?=\w{5})(?=\D*\d{2})/i;
I have tested my suggested changes locally and everything seems to work fine.
However, after thinking about it a bit more, I think the instructions technically do need to be modified in one way, since all of the tests and last example talk pertain to passwords with only numbers and letters. I think the instructions to add one extra requirement that only alpha-numeric character and the underscore character should be allowed. Maybe, the instructions could read like:
Use two lookaheads
in the pwRegex
to match passwords that are greater than 5 characters long, do not begin with numbers, and have two consecutive digits. Passwords should only include alpha-numeric characters and the underscore character (_
) as valid characters.
Also, something else bothers me about the example given in the challenge description (see below).
A more practical use of lookaheads
is to check two or more patterns in one string. Here is a (naively) simple password checker that looks for between 3 and 6 characters and at least one number:
let password = "abc123";
let checkPass = /(?=\w{3,6})(?=\D*\d)/;
checkPass.test(password); // Returns true
Technically, the given regular expression would incorrectly match passwords with more than six letters like "sdgslk33aa".
The suggestion you've given for the regex would fail a2si34
which should pass, it is 6 characters, doesn't start with a digit, is more than 5 characters and has 2 consecutive digits
@thecodingaviator Good catch. What about the following?
let pwRegex = /^[a-z](?=\w{5})(?=\w*\d{2})/i;
@RandellDawson That one looks good. About reworking the instructions, I have a small suggestion:
Use two
lookaheads
in thepwRegex
to match passwords that are greater than 5 characters long, do not begin with numbers or special characters, and have two consecutive digits. Passwords should only include alpha-numeric characters and the underscore character (_
) as valid characters.
This way we don't have to go over changing the solution you posted above to make it not fail if a password starts with an _
character
@thecodingaviator That sounds good. Let's wait until we get some other mods and other users to respond before we create a PR. I have asked a couple of people on the forum to review this issue.
EDIT: Actually, I think the instructions can just be the following:
Use two
lookaheads
in thepwRegex
to match passwords that only include alpha-numeric characters and the underscore character (_), are greater than 5 characters long, do not begin with numbers, and have two consecutive digits.
Then, the solution regex would just add _
in the character class [a-z_]
. Then, the first character can be an underscore.
I agree that the fixes above are 'correct', however I would like to caution against making the solution's regular expression more complicated.
Instead I think the right thing to do is remove the text in the question regarding the first character. Are we validating javascript variable names, or are we validating passwords?
At this point the challenge is very confusing for a beginner, especially compared to the prior regular expression questions. Not only do they need to figure out how lookaheads work, they also need to use two of them, and figure out how to 'skip' in a sense looking immediately at the start of the string for the numbers.
Adding additional requirements adds more overhead to the challenge that they've already learnt about, I'm not entirely sure that's wise.
The main problem with this challenge right now in my opinion is that to someone new to regular expressions our explanation of why the \D*
is there is not illuminating at all
It's one of the most commonly asked questions about something non-trivial I've seen on the forums, and probably for good reason. The users also tend to ask specifically also about the solution itself, so they've clearly been stuck enough to look.
I've found myself answering questions each time with some variant of (copied here from one of my longer answers):
Imagine you are a regex engine, and you’ve been given a regex command and a string to check. You walk through the string, examining at each step the current character you’re in front of. You take another step each time to look at the next character.
Imagine now you’re told to look ahead. This means you examine the next few characters to look for a match without moving your position
You look ahead, and check that, yes/no (positive/negative), the next few characters satisfy the lookahead, and continue trying to match the rest of the regex in the usual way
When we have two lookaheads at the same time, we are looking from the same position. That’s why it almost seems like there’s a logical AND there, because we check the first lookahead, check the second lookahead, and then try match the rest of the regex
The reason that \D* was needed, is because when looking ahead to check there’s two digits, we don’t care about them being right in front of us, so we are told “0 or more non-digits right in front of us, with two digits”
I think that while we're working on a PR for the incorrect tests and solution we really need to update the _explanation_ with the solution - I think this would go a long way with helping beginners and avoid the majority of the questions about it
Apologies if this remark is slightly off-topic and apologies for the length
@gebulmer Thank you for your valuable input. I have gone back in forth in my head about what is best for the curriculum.
I absolutely agree that the challenge (regex) becomes much simpler if we remove the requirement of "do not begin with numbers". With that removed, I think a valid solution for the regex could be the following:
/(?=.{6})(?=.*\d{2})/
However, since this challenge is near the end of the Regular Expressions section, I think with the changes I have recommended in the instructions, the solution I created should still achievable by a camper at this stage. This is especially true due to limiting the characters to alpha-numeric and the underscore character. That way they can make use of the \w
in the expression.
I could go either way on this challenge. I definitely agree that no matter which way we go, the solution in the Guide should have a very detailed explanation of how the regex solves the challenge.
@gebulmer I went through that, thank you for that descriptive explanation. @RandellDawson and I had a chat yesterday about this and my stand is that we should remove the requirement for the first digit to not be a number. Maybe we need one more person's stand on this before we come to a conclusion
I'm leaning towards removing the requirement as well. According to the style guide, a challenge should not take longer than 2 minutes to solve. I took a lot longer than that and my first solution did not match @thecodingaviator's password, either.
I agree with @RandellDawson that someone who is that far through the regex section should be able to solve it. I just don't think it's likely they will be able to do it in a reasonable amount of time.
The challenge says to "Use lookaheads in the pwRegex to match passwords that are greater than 5 characters long and have two consecutive digits." However, {5,}, meaning "at least 5" rather than "greater than 5" is shown in the solution and accepted. While {6,} is also accepted, I think {5,} shouldn't be accepted or shown in the solution.
If you look at the current solution on master it is {6,}
.
123456 or any other 6+ numeric string could be added as a test. Using a slightly modified regex from the accepted solution, with a * (0 or more match) on the non-digit (\D), results in the current tests passing but the objective not being met,
Using a slightly modified regex from the accepted solution, with a * (0 or more match) on the non-digit (\D), results in the current tests passing but the objective not being met,
@dalevross What is the exact regex that passes all the current tests but does not meet the current instructions' requirements?
/^(?=\w{6})(?=\D*\d{2})/
@RandellDawson Specifically, any string that starts with 2 numeric digits and a length greater than 5 will match that regex due to the second lookahead matching on 0 instances of a non-digit character.
I have tried many different things, but this worked for me:
/^\D(?=\w{6})(?=\w+\d{2})/g
is this still a wanted change for the curriculum?
one thing it is missing is that the lookaheads look from the same position, this is a thing I have often to explain on the forum - adding this in the description, or anyway in a way that is less criptic than "will look for but not match" could be an improvement
various tests have been added since then, to check that the created regular expression correctly make sure that the string doesn't start with a number, so many comments are outdated here
One thing that has always irked me is "that are greater than 5 characters long", some people interpret this as minumum length = 5, and others as minimum length = 6, but there is no tests at 5 characters to know which ones of the two is true
a thing like "ban11" should be accepted or rejected?
let sampleWord = "astronaut";
let pwRegex = /^a-z(?=\w*\d{2})/i;
let result = pwRegex.test(sampleWord);