Count valid passwords. E.g. the first row of the sample input says: the password
abcde
must contain the lettera
1–3 times.
Regex to the rescue:
const input = `
1-3 a: abcde
1-3 b: cdefg
2-9 c: ccccccccc
`
.trim()
.split('\n')
const validPasswords = input.filter((row) => {
const { min, max, letter, password } = row.match(
/(?<min>\d+)-(?<max>\d+) (?<letter>[a-z]): (?<password>[a-z]+)/
).groups
const occurrences = password.match(new RegExp(letter, 'g'))?.length
return occurrences >= min && occurrences <= max
})
console.log(validPasswords.length)
Count valid passwords. E.g. the first row of the sample input says: the password
abcde
must contain the lettera
either in position 1 or in position 3. (The first letter is in position 1, not 0.)
We can use the same regex, but let's rename the first two capturing groups:
const validPasswords = input.filter((row) => {
const { firstPos, secondPos, letter, password } = row.match(
/(?<firstPos>\d+)-(?<secondPos>\d+) (?<letter>[a-z]): (?<password>[a-z]+)/
).groups
const firstMatch = password[firstPos - 1] === letter
const secondMatch = password[secondPos - 1] === letter
return firstMatch !== secondMatch
})
console.log(validPasswords.length)
I used named capturing groups in the regexes for the first time. They were easy to use but made the regexes more readable, me thinks. Guess I'll be using them more from now on.
On a second thought, I don't know if they provided much benefit in this particular case:
const { min, max, letter, password } = row.match(
/(?<min>\d+)-(?<max>\d+) (?<letter>[a-z]): (?<password>[a-z]+)/
).groups
// versus
const [, min, max, letter, password] = row.match(
/(\d+)-(\d+) ([a-z]): ([a-z]+)/
)
const { firstPos, secondPos, letter, password } = row.match(
/(?<firstPos>\d+)-(?<secondPos>\d+) (?<letter>[a-z]): (?<password>[a-z]+)/
).groups
// versus
const [, firstPos, secondPos, letter, password] = row.match(
/(\d+)-(\d+) ([a-z]): ([a-z]+)/
)
But I'm sure they can be useful in more complex cases.