DEV Community

Cover image for Doesn't He Have Intern-Elves For This?
Robert Mion
Robert Mion

Posted on

Doesn't He Have Intern-Elves For This?

Advent of Code 2015 Day 5

Part 1

  1. The final - and first - regex gauntlet?
  2. A trio of regex
  3. reduce() and every() to wrap it all up

The final - and first - regex gauntlet?

  • Input is a list of strings
  • Some strings are valid
  • Others are not
  • Discerning a string's proper category likely requires the use of one or more regular expressions

There have been several thus far.

They've all been preparing me for this one!

A trio of regex

contains at least three vowels

The regex to match a single vowel character is:

/[aeiou]/
Enter fullscreen mode Exit fullscreen mode

When combined with matchAll(), the number of matches better be at least 3:

[...text.matchAll(/[aeiou]/)].length >= 3
Enter fullscreen mode Exit fullscreen mode

contains at least one letter that appears twice in a row

The regex to match any letter twice in a row is:

/(\w)\1/
Enter fullscreen mode Exit fullscreen mode

When combined with matchAll(), the number of matches better be at least 1:

[...text.matchAll(/(\w)\1/)].length > 0
Enter fullscreen mode Exit fullscreen mode

does not contain the strings ab, cd, pq, or xy, even if they are part of one of the other requirements

The regex to match ab, cd, pq or xy is:

/ab|cd|pq|xy/
Enter fullscreen mode Exit fullscreen mode

When combined with matchAll(), the number of matches better be 0:

[...text.matchAll(/ab|cd|pq|xy].length == 0
Enter fullscreen mode Exit fullscreen mode

reduce() and every() to wrap it all up

  • reduce() to accumulate a count of nice strings
  • every() to ensure all of the tests passed

My algorithm in JavaScript:

input.reduce(
  (nice, text) => 
    nice += [
      [...text.matchAll(/[aeiou]/)].length >= 3,
      [...text.matchAll(/(\w)\1/)].length > 0,
      [...text.matchAll(/ab|cd|pq|xy/)].length == 0
    ].every(el => el == true) ? 1 : 0
, 0)
Enter fullscreen mode Exit fullscreen mode

It generated the correct answer!

Part 1, a year ago

  • When I discovered Advent of Code, I attempted a few Days' puzzles, starting with 2015
  • Day 5 was where I stopped
  • I only completed Part 1

Here's how I did it back then.

contains at least three vowels

Count each vowel, character by character:

function hasMin3Vowels(str) {
  let vowels = ['a','e','i','o','u']
  let numVowels = 0
  str.split("").forEach(char => {
    if (vowels.includes(char)) {
      numVowels += 1
    }
  })
  return numVowels >= 3
}
Enter fullscreen mode Exit fullscreen mode

contains at least one letter that appears twice in a row

Check for where the next character matches the current one:

function hasOneLetterTwiceInARow(str) {
  let matches = 0
  str.split("").forEach((char, idx) => {
    if (idx !== str.length - 1) {
      if (char == str[idx + 1]) {
        matches += 1
      }
    }
  })
  return matches > 0
}
Enter fullscreen mode Exit fullscreen mode

does not contain the strings ab, cd, pq, or xy, even if they are part of one of the other requirements

Check the string for the existence of any bad pair:

function hasNoDisallowedStrings(str) {
  let output = 0;
  let disallowedStrings = ['ab', 'cd', 'pq', 'xy']
  disallowedStrings.forEach(bad => {
    if (str.indexOf(bad) !== -1) {
      output += 1
    }
  })
  return output == 0;
}
Enter fullscreen mode Exit fullscreen mode

A wrapper function:

function isNice(str) {
    return hasMin3Vowels(str) && hasOneLetterTwiceInARow(str) && hasNoDisallowedStrings(str)
}
Enter fullscreen mode Exit fullscreen mode

A wrapping iterator:

input.map(str => isNice(str))
  .filter(str => str == true)
  .length
Enter fullscreen mode Exit fullscreen mode

Looking bad on this code, I'm very proud of how I solved it this round.

Part 2

A duo of regex

contains a pair of any two letters that appears at least twice in the string without overlapping

The regex to match a consecutive, non-overlapping letter pair is:

/(\w)(\w).*\1\2/
Enter fullscreen mode Exit fullscreen mode

When combined with matchAll(), the number of matches better be at least 1:

[...text.matchAll(/(\w)(\w).*\1\2/)].length > 0
Enter fullscreen mode Exit fullscreen mode

contains at least one letter which repeats with exactly one letter between them

The regex to match a three characters in the pattern A*A is:

/(\w)(\w)\1/
Enter fullscreen mode Exit fullscreen mode

When combined with matchAll(), the number of matches better be at least 1:

[...text.matchAll(/(\w)(\w)\1/)].length > 0
Enter fullscreen mode Exit fullscreen mode

My updated algorithm in JavaScript:

input.reduce(
  (nice, text) => 
    nice += [
      [...text.matchAll(/(\w)(\w).*\1\2/)].length > 0,
      [...text.matchAll(/(\w)(\w)\1/)].length > 0
    ].every(el => el == true) ? 1 : 0
, 0)
Enter fullscreen mode Exit fullscreen mode

It generated the correct answer!

I did it!!

  • I solved both parts!
  • Using five different regex!

This puzzle was undeniable proof of my newfound comfortability crafting regular expressions.

When I attempted it nearly a year ago - after first discovering AoC - I barely solved Part 1 using non-regex methods...and didn't even attempt Part 2.

On this day, however, I completed this puzzle in under a half hour. It felt almost easy!

Well done, self. Well done.

Top comments (0)