Doesn't He Have Intern-Elves For This?
Tags: adventofcode, algorithms, regex, javascript
Published: 2022-09-22 00:45:28

## [Advent of Code 2015 Day 5](https://adventofcode.com/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 expression`s

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]/
```

When combined with `matchAll()`, the number of matches better be at least 3:
```js
[...text.matchAll(/[aeiou]/)].length >= 3
```

> contains at least one letter that appears twice in a row

The `regex` to match any letter twice in a row is:
```
/(\w)\1/
```

When combined with `matchAll()`, the number of matches better be at least 1:
```js
[...text.matchAll(/(\w)\1/)].length > 0
```

> 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/
```

When combined with `matchAll()`, the number of matches better be 0:
```js
[...text.matchAll(/ab|cd|pq|xy].length == 0
```

### `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:
```js
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)
```

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:
```js
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
}
```


> contains at least one letter that appears twice in a row

Check for where the next character matches the current one:
```js
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
}
```


> 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:
```js
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;
}
```


A wrapper function:
```js
function isNice(str) {
    return hasMin3Vowels(str) && hasOneLetterTwiceInARow(str) && hasNoDisallowedStrings(str)
}
```


A wrapping iterator:
```js
input.map(str => isNice(str))
  .filter(str => str == true)
  .length
```

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/
```

When combined with `matchAll()`, the number of matches better be at least 1:
```js
[...text.matchAll(/(\w)(\w).*\1\2/)].length > 0
```

> 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/
```

When combined with `matchAll()`, the number of matches better be at least 1:
```js
[...text.matchAll(/(\w)(\w)\1/)].length > 0
```

My updated algorithm in JavaScript:
```js
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)
```

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.