Advent of Code 2018 Day 2 Part 1 - Solution and Explanation
benboorstein
Posted on August 14, 2022
The challenge itself can be found here.
Please note that:
- This article focuses on only one solution to a challenge to which there are multiple solutions.
- The solution in its entirety can be found at the bottom.
- The solution utilizes a main function, objects, loops, conditionals, array methods, and the
Object.values()
method.
Because the challenge's actual "puzzle input" is very long, we will be working with the challenge's example "puzzle input":
abcdef
bababc
abbcde
abcccd
aabcdd
abcdee
ababab
Before we can get to the main part of the challenge, we have to convert what's just above into JavaScript, so that we can then convert it into an array of strings (each string will be a box ID).
So how do we interpret what the seven lines of text are? In other words, as they are currently, how can we translate (so to speak) them into JavaScript?
Well, we consider it to be one long string with line breaks:
'abcdef\nbababc\nabbcde\nabcccd\naabcdd\nabcdee\nababab'
How can we be sure this is correct?
Well if we log the string to the console, what we'll see printed is the original input:
abcdef
bababc
abbcde
abcccd
aabcdd
abcdee
ababab
So we know it's correct.
Let's save the string into a variable called examplePuzzleInput
.
const examplePuzzleInput = 'abcdef\nbababc\nabbcde\nabcccd\naabcdd\nabcdee\nababab'
Next, let's...
- use the
split()
method to convert this long string into an array of strings - store our result in a variable called
boxIDsArr
const boxIDsArr = examplePuzzleInput.split('\n')
What does boxIDsArr
log?
['abcdef', 'bababc', 'abbcde', 'abcccd', 'aabcdd', 'abcdee', 'ababab']
So now we have our array and we can move on to the main part of the challenge.
Our task is to...
- count the number of box IDs that have exactly 2 of any letter
- count the number of box IDs that have exactly 3 of any letter
- do this to get the checksum: number of doubles * number of triples
(If in need of more clarity, the challenge itself provides some explanation.)
First we'll set up a function called getMatches
that takes the parameter input
. Input
is the alias we'll use for the array of box IDs that's shown above.
function getMatches(input) {
}
Because we want to count...
- the number of box IDs that have exactly TWO of any letter
- the number of box IDs that have exactly THREE of any letter
...the next thing we'll do is set up an object, called matches
, to keep count of the number of matches found for each of these two categories.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
}
Because we need to access each of the input
array's strings, we're going to loop through input
with a forEach
loop. Each value we'll be accessing in this loop is a box ID string, so we'll call it boxIdStr
.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
})
}
But what we really need to be able to access is each letter in each string. So, since we're already in each string, we're now going to loop through each string with another forEach
loop.
Note that we're putting the split()
method on each string to convert the string to an array of letters (with each letter being a string). So boxIdStr.split('')
for the input
array's first string (the first string is: 'abcdef'
) looks like this:
['a', 'b', 'c', 'd', 'e', 'f']
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
boxIdStr.split('').forEach(letter => {
})
})
}
Because we're going to be counting the number of occurrences of each letter in each string, the next thing we'll do is set up another object, this time called counts
, which will start as an empty object.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
})
})
}
How are we going to count the number of occurrences of each letter in each string?
Let's take the second string in the input
array (the second string is: 'bababc'
) as an example. Having been converted to an array of letters, it looks like this:
['b', 'a', 'b', 'a', 'b', 'c']
As the loop hits 'b'
(the first letter in the string):
If counts[letter]
(in other words, counts['b']
) does exist (in other words, if the counts
object does have a key called b
), then increment b
's value by 1. But if counts[letter]
(in other words, counts['b']
) does not yet exist (in other words, if the counts
object does not yet have a key called b
), then give the counts
object a key called b
and give b
a value of 1.
In the code just below, note that the if
clause's counts[letter]
is a shorthand for counts.hasOwnProperty(letter)
which is a shorthand for counts.hasOwnProperty(letter) === true
. The reason counts[letter]
works here is because if it returns undefined
, then it is returning something that is falsy
, and if it returns a value, then it is returning something that is truthy
.
Also note that we could replace the below code's if...else
statement with counts[letter] = (counts[letter] || 0) + 1
. It's just a more concise way of accomplishing the same thing.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
})
}
Note that console.log(counts)
is only included in the code above so that we can see exactly what the counts
object for each string looks like once everything that is going to be added to it has been added to it. For example, the counts
object for the second string looks like this:
{b: 3, a: 2, c: 1}
So we know that, in the second string, a
appears two times, b
appears three times, and c
appears one time.
Of course what we've explored here with the second string has been repeated for every string in the input
array.
Now we need to determine for each counts
object if it does or does not contain values that are 2
or values that are 3
. Notice that what we are not doing is counting the number of values in each counts
object that are 2
or 3
. (In case this distinction is still unclear, it's described in more detail farther down.)
We accomplish this by using the includes()
method.
Because includes()
returns true
or false
, Object.values(counts).includes(2)
, for example, is not concerned with how many of a counts
object's values are 2
but rather whether or not this counts
object has any values that are 2
.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
}
if (Object.values(counts).includes(3)) {
}
})
}
And if a count
object contains values of 2
(regardless of how many of the object's values are 2
), we will increment the value of the matches
object's twice
key by 1.
In other words, with such an object as this {a: 1, b: 2, c: 1, d: 1, e: 1}
, the matches
object's twice
key's value will increment by 1, and this is probably pretty obvious. But what may not be obvious is that even with such an object as this {a: 2, b: 1, c: 1, d: 2}
, the matches
object's twice
key's value will increment by 1, not 2. Again, this is due to how includes()
works.
And if a count
object contains values of 3
(regardless of how many of the object's values are 3
), we will increment the value of the matches
object's thrice
key by 1.
In other words, with such an object as this {a: 1, b: 1, c: 3, d: 1}
, the matches
object's thrice
key's value will increment by 1, and this is probably pretty obvious. But what may not be obvious is that even with such an object as this {a: 3, b: 3}
, the matches
object's thrice
key's value will increment by 1, not 2. Again, this is due to how includes()
works.
And it probably doesn't need to be said but, because more clarity is better, let's include it: With such an object as this {a: 2, b: 3, c: 1, d: 1, e: 3, f: 2, g: 3}
, the matches
object's twice
key's value and thrice
key's value will each only be incremented by 1.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
matches.twice++
}
if (Object.values(counts).includes(3)) {
matches.thrice++
}
})
}
Because what we're after is the checksum and the way to get the checksum is to calculate number of doubles * number of triples, our next step is to write this calculation in our function and return it.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
matches.twice++
}
if (Object.values(counts).includes(3)) {
matches.thrice++
}
})
return matches.twice * matches.thrice
}
Now for our final step: Call the getMatches
function, passing in boxIDsArr
, and log that function call to the console.
function getMatches(input) {
let matches = {
twice: 0,
thrice: 0
}
input.forEach(boxIdStr => {
let counts = {}
boxIdStr.split('').forEach(letter => {
if (counts[letter]) {
counts[letter]++
} else {
counts[letter] = 1
}
})
console.log(counts)
if (Object.values(counts).includes(2)) {
matches.twice++
}
if (Object.values(counts).includes(3)) {
matches.thrice++
}
})
return matches.twice * matches.thrice
}
console.log(getMatches(boxIDsArr)) // 12 // 12 is the checksum
I hope this has been clear and helpful!
Thank you for reading!
Posted on August 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.