# Completed Advent Of Code: Day One

Updated On

Advent of Code 2023 is here and I just completed its day-one challenge. Overall, it was fun to play with strings and numbers in javascript. Without any further ado, let's explore the solution.

The challenge is divided into two parts. The first part is quite simple but the second one adds a cherry on top of the cake.

## Part 1 ^{#}

From a given list of strings, I had to find the digits, combine the first and last digit in order of occurrence in the string and them sum it up all. To simplify, for a given string `gh34s1`

, the output will be `31`

with `3`

as the first digit, `1`

as the last digit and `31`

as their concatenation.

So, for two strings `gh34s1`

and `d7hs23`

the sum of their digits will be `31 + 23 = 54`

.

I stored the input in a `.txt`

file and then looped over line by line to get the string from each line.

```
import readline from 'readline'
import fs from 'fs'
import path from 'path'
async function readFileByLine() {
const lines = []
const readInterface = readline.createInterface({
input: fs.createReadStream(path.resolve('day-1/input.txt')),
output: process.stdout,
terminal: false
});
for await (const line of readInterface) {
lines.push(line)
}
return lines
}
(async () => {
const lines = await readFileByLine()
// code
})()
```

Then, to parse the digits in the string and to validate if they are numbers, I used zod. There was a simple way to sort the first and last digits but I couldn't figure it out until I started with Part-2.

```
const calibrationByLine = lines.map((line, i) => {
const lineArr = line.split('')
const firstAndLastNumber = {
first: null,
last: null
}
for (const [index, char] of lineArr.entries()) {
if (!z.nan().safeParse(parseInt(char, 10)).success) {
firstAndLastNumber.first === null ? firstAndLastNumber.first = char : firstAndLastNumber.last = char;
}
};
firstAndLastNumber.last === null && (firstAndLastNumber.last = firstAndLastNumber.first);
return parseInt(`${firstAndLastNumber.first}${firstAndLastNumber.last}`, 10)
})
const result = calibrationByLine.reduce((acc, curr) => acc + curr, 0)
console.log(result)
```

This approach gave me the correct answer for sure but there was a better approach. Yes, regex!

## Part-2 ^{#}

Part-2 had me thinking that my solution was correct, but in fact it wasn't. Lets see what was it. So, the second part introduced a twist in the story, not only the digits were represented as integers in the string, but also their alphabetical synonyms ("one", "two", "three") were also a part of it.

Regex was the answer. I tried `/(one|two|three|four|five|six|seven|eight|nine)/g`

as a regular expression along with `matchAll()`

to find all occurrences of the alphabetical number representations and it seemed to work.

Then, I stored the digits along with their indexes in an array of objects by mapping the alphabetical numbers to their respective integers and sorted them in an ascending order based on the index to get the first and last digits.

```
const regex = new RegExp('(one|two|three|four|five|six|seven|eight|nine)', 'g')
const stringToIntMap = new Map([["one", 1], ["two", 2], ["three", 3], ["four", 4], ["five", 5], ["six", 6], ["seven", 7], ["eight", 8], ["nine", 9]])
const calibrationByLine = lines.map((line, i) => {
const allDigitsInLine = []
for (const match of line.matchAll(regex)) {
allDigitsInLine.push({
digit: stringToIntMap.get(match[1]),
index: match.index
})
}
const lineArr = line.split('')
const firstAndLastNumber = {
first: {
digit: null,
index: null
},
last: {
digit: null,
index: null
}
}
for (const [index, char] of lineArr.entries()) {
if (!z.nan().safeParse(parseInt(char, 10)).success) {
allDigitsInLine.push({
digit: char,
index
})
}
};
const sortedDigitsAccordingToIndex = allDigitsInLine.sort((a, b) => a.index - b.index)
firstAndLastNumber.first = {
...sortedDigitsAccordingToIndex[0]
}
firstAndLastNumber.last = {
...sortedDigitsAccordingToIndex[sortedDigitsAccordingToIndex.length - 1]
}
firstAndLastNumber.last.digit === null && (firstAndLastNumber.last.digit = firstAndLastNumber.first.digit);
return parseInt(`${firstAndLastNumber.first.digit}${firstAndLastNumber.last.digit}`, 10)
})
const result = calibrationByLine.reduce((acc, curr) => acc + curr, 0)
console.log(result)
```

What it would do is, for a string like `d3two4eight`

, the first digit it will select is `3`

and the last as `8`

. So, the output for this string would be `38`

. So far so good. I submitted the answer and it was incorrect. There was a catch.

The input also contained strings such as `3eightwo`

. The alphabetical parts of `eight`

and `two`

overlap. And so, my assumption was to ignore the latter digit and just keep `eight`

which would make the output as `38`

. But the correct output should be `32`

after taking into account the overlapping representations.

After a bit of googling I found out about how to find overlapping matches using the lookahead assertion in regular expressions.

Modified the regex to this and I got the perfect answer.

`new RegExp('(?=(one|two|three|four|five|six|seven|eight|nine))', 'gm')`

And that's it. Through this challenge I learned about regex more than I could have normally. Here's the entire solution:

```
import readline from 'readline'
import fs from 'fs'
import path from 'path'
import { z } from 'zod'
const regex = new RegExp('(?=(one|two|three|four|five|six|seven|eight|nine))', 'gm')
const stringToIntMap = new Map([["one", 1], ["two", 2], ["three", 3], ["four", 4], ["five", 5], ["six", 6], ["seven", 7], ["eight", 8], ["nine", 9]])
async function readFileByLine() {
const lines = []
const readInterface = readline.createInterface({
input: fs.createReadStream(path.resolve('day-1/input.txt')),
output: process.stdout,
terminal: false
});
for await (const line of readInterface) {
lines.push(line)
}
return lines
}
(async () => {
// reading file line by line and storing it in an array - each line contains a string
const lines = await readFileByLine()
// looping over each string
const calibrationByLine = lines.map((line, i) => {
const allDigitsInLine = []
// finding alphabetical digits, mapping them to their integers, and storing them along with index
for (const match of line.matchAll(regex)) {
allDigitsInLine.push({
digit: stringToIntMap.get(match[1]),
index: match.index
})
}
const lineArr = line.split('')
const firstAndLastNumber = {
first: {
digit: null,
index: null
},
last: {
digit: null,
index: null
}
}
// finding integers and storing them along with index
for (const [index, char] of lineArr.entries()) {
if (!z.nan().safeParse(parseInt(char, 10)).success) {
allDigitsInLine.push({
digit: char,
index
})
}
};
// sorting integers based on the index - ascending
const sortedDigitsAccordingToIndex = allDigitsInLine.sort((a, b) => a.index - b.index)
firstAndLastNumber.first = {
...sortedDigitsAccordingToIndex[0]
}
firstAndLastNumber.last = {
...sortedDigitsAccordingToIndex[sortedDigitsAccordingToIndex.length - 1]
}
firstAndLastNumber.last.digit === null && (firstAndLastNumber.last.digit = firstAndLastNumber.first.digit);
// concatenating digits and converting into integer
return parseInt(`${firstAndLastNumber.first.digit}${firstAndLastNumber.last.digit}`, 10)
})
// summing up the result of every string
const result = calibrationByLine.reduce((acc, curr) => acc + curr, 0)
console.log(result)
})()
```

The execution time for `1000`

strings turns out to be:

`executed: 94.047 ms`