Advent of Code is a series of software puzzles released every year during Adventadvent. There's a cute Christmas-themed story that ties the puzzles together. Every day at midnightmidnight, a link to the day's first puzzle goes live on the Advent of Code site. When you solve that puzzle, you get a link to a second puzzle which builds on the first. The problems are great examples of the kinds of algorithmic challenges that can come up in large scale production code, but almost never happen in personal projects. That means they resemble the whiteboard job interview stuff that nobody I know likes. The critical difference is that Christmas is a much more enjoyable theme than My Career and Therefore Life Are At Stake Why Can't I Do This Damn You Whiteboard Damn You to Hell.
I think the Advent of Code is especially well-suited for bootcamp grads who have been out for a while.
The first problem of Day One amounts to reading a set of numerical frequency changes and calculating where the frequency ends up. Quoting from the site:
For example, if the device displays frequency changes of+1, -2, +3, +1, then starting from a frequency of zero,the following changes would occur:===Current frequency 0, change of +1; resulting frequency 1.Current frequency 1, change of -2; resulting frequency -1.Current frequency -1, change of +3; resulting frequency 2.Current frequency 2, change of +1; resulting frequency 3.In this example, the resulting frequency is 3.===Here are other example situations:+1, +1, +1 results in 3+1, +1, -2 results in 0-1, -2, -3 results in -6
The problem includes a link to a file that just has numbers, some positive and some negative, each on its own line, like the following Example Data
-19+3+7-1+21
OK, here's how I solved it:
The first problem I have is that JavaScript doesn't see things the way we do. When I open the file that has numbers in it, it looks to me like a list of numbers. But to JavaScript, it's just a file. To get anywhere, I need to get JavaScript to recognize the list of numbers in the file.
First I saved the file with the numbers as day1.txt. Then I opened a new file, day1.js, and wrote some Node code to read the data file:
const fs = require('fs')const input = fs.readFileSync('./input/day1.txt', 'utf8').trim()
The first line lets me use the Node module fs, which handles file operations synchronously (so I don't have to deal with asynchronous stuff like promises or async/await). The second line reads that file and holds it in a variable called input, which is one long, multi-line string.
That's better. I know more about dealing with strings in JavaScript than I do about dealing with files.
The next thing I want to do is to turn the one string into a bunch of numbers. I know input is a multi-line string, with each number represented on its own line. I'd like to split the string into a lot of pieces, where each piece represents one line. I know the end of a line in JavaScript is represented by a newline character, \n. I also know there's a method that can be invoked on a JavaScript string that splits it up:
const sequenceOfStrings = input.split('\n')// sequenceOfStrings is an array of strings
Since input is a string with a "number" on each line, input.split("\n") returns an array with the "number"s as elements. The quotes around "number" are there to emphasize that at this point, they're not actual numbers yet - they're still strings, like input was. If I was working with the Example Data I showed above, my sequenceOfStrings would look like ["-19", "+3", "+7", "-1", "+21"]. So far, I managed to turn a file into a string, and then turn that one string into an array of strings. Now I want to get an array, but instead of an array of strings, I want an array of numbers.
In order to get the array I want, I need to transform every element in the array I have - transform from a string that looks like a number, into the number that the string looks like:
/*elements in */ /*elements in *//*array I have*/ /*array I want*/"-19" -19"+3" 3"+7" 7"-1" -1"+21" 21
I want to
The way to do that in JavaScript is by using the map method on the array I have. I'll also need a function to do each of the transformations. There's a built-in function in JavaScript that does what I want:
Number('-19') // returns -19Number('+3') // returns 3
I can use the Number function together with the map method to turn the array of strings I have into the array of numbers I want. You may have seen map illustrated by examples like this, which transforms each element in the array by multiplying it by five:
const multiples = [1, 2, 3].map(n => n * 5) // returns [5, 10, 15]
where the transformation function is written as an arrow function n => n * 5. With that in mind, I could write my transformation using my example array and the Number function like so:
const numbers = ['-19', '+3', '+7', '-1', '+21'].map(n => Number(n))
That transformation function, n => Number(n), has a parameter n, and returns whatever Number(n) is. But I could say the same thing about Number by itself. It's a function that takes a parameter n and returns Number(n). That means I can write my transformation a little more concisely:
const numbers = ['-19', '+3', '+7', '-1', '+21'].map(Number)
When I realized I could do that, it immediately became my favorite thing about JavaScript. I just feel so baller when I can write such short, expressive...expressions. Anyway, that's how to transform the array of strings into an array of numbers. There's just one more step.
Turning an array into a single value is the reason for the reduce method, and adding all the numbers in an array into a single sum is probably the most common example used to illustrate reduce:
const sum = [-19, 3, 7, -1, 21].reduce((total, current) => total + current, 0)
The way reduce works is, it takes two arguments, a callback function and an initial value. In calculating sum, the initial value is 0, and (total, current) => total + current is the callback function. As you can see, the callback function has two parameters, which represent an accumulator (something that keeps track of data) and an element from the array. 1 Every element in the array takes a turn being current. The callback function does whatever it has to do to produce a return value (for sum that just means adding total and current), and that return value becomes the new total. If total is always computed with the previous element in the array, there's no way to compute it for the first element in the array, and that's why there's an initial value - it's the initial value of total.
To sum up (see what I did there?), sum would be calculated like this:
// initial value | 1st element in array | next total0 + -19 = -19// total from above | 2nd element in array | next total-19 + 3 = -16// total from above | 3rd element in array | next total-16 + 7 = -9// total from above | 4th element in array | next total-9 + -1 = -10// total from above | 5th element in array | next total-10 + 21 = 11
And that's the last step in solving Part One. All of the above writing can be summed up with these lines of code:
const fs = require('fs')const input = fs.readFileSync('./input/day1.txt', 'utf8').trim()const sequenceOfStrings = input.split('\n')const sequenceOfNumbers = sequenceOfStrings.map(Number)const answer = sequenceOfNumbers.reduce((total, current) => total + current, 0)
Or without the intermediate variables:
const fs = require('fs')const answer = fs.readFileSync('./input/day1.txt', 'utf8').trim().split('\n').map(Number).reduce((total, current) => total + current, 0)
Looking back, the most important things I needed to know to get to my solution were that the answer had to be a single number, the initial data was in a file, and there are ways to get from one data type to another.
Part Two gets tougher.
reduce can take between one and four parameters, but I'm giving it two here.↩