Advent of Code 2020 Day 6 - Solution + Tutorial (TypeScript)

published
All TypeScript Solutions for Advent of Code 2020

I'll try to update asap. Please bear in a mind that I'll probably lag one or multiple days behind.

Prerequisites

I assume you've put your puzzle input into an array called lines where each array item is a line of the input text file. It's up to you to either parse the text file or create an array by hand.

const lines = [
  "cedziyl",
  "lnemy",
  "",
  "oujdnhgwlqfbmpcrevktaizs",
  "covqbthupgradfnijslexwk",
  "",
  "eawjhlxrtc",];

Solution

Puzzle

Just to make sure, you know what I'm talking about, take a look at today's puzzle:

Day 6: Custom Customs

Part 1

This time, we should check the answers to the customs declaration forms. We are given the answers for groups of people. For part 1, for each group, we'll have to count the number of questions to which anyone in the group answered "yes".

First, let's look at the input again. The data per group is split over several lines. Let's merge this data similar to like we did it on Day 4: Passport Processing.

We want that every item in the group array represents exactly one group. So that

"cedziyl",
"lnemy",
"",
"oujdnhgwlqfbmpcrevktaizs",
"covqbthupgradfnijslexwk",
"",
"eawjhlxrtc",

becomes

["cedziyl", "lnemy"],
["oujdnhgwlqfbmpcrevktaizs", "covqbthupgradfnijslexwk"], 
["eawjhlxrtc",],

That would make the data much easier to work with. Let's go. We have our array lines. We can transform this to the groups array we want.

const groups = lines
  .join("\n")
  .split("\n\n")
  .map((group) => group.split("\n"));

Nice! First, we join all lines with newlines. Then, we split if there is a double newline. That tells us, that the data for a new group begins. Now we have the data for each group in a single line. Let's split this data by using the newlines.

Our groups array now looks like this:

const groups = [
  ["cedziyl", "lnemy"],
  ["oujdnhgwlqfbmpcrevktaizs", "covqbthupgradfnijslexwk"], 
  ["eawjhlxrtc",],];

You could say its type is string[][]. It's an array of string arrays.

Good. Now it's much easier to work with the data. What should we do again? Basically, we want to find out how many unique answers (characters) a group has given. These counts per group should be added together, and the puzzle is solved.

Okay, so we have to do something per group. How can we find the unique characters per group. Some of you may think that we should use something like the lodash library. It exports a function called .uniq. Well, yeah, that would be possible. However, let's solve it without using external dependencies.

Good thing TypeScript has a data structure that fits our use-case. We can make use of a Set. Look:

const set = new Set(["a", "c", "d", "c"]);

This would result in a Set of size 3. Why? Because a set holds unique values. No duplicate values are allowed. So the set's contents are a, c, d. Nice, this way we don't need external dependencies like lodash.

Now let's apply this to our groups.

groups
  .map((group) => {
    const set = new Set([...group.join("")]);
    return set.size;
  })

Wow, there might be happening a bit too much for you. I'll try to explain:

First, we want to transform our groups so that we know the count of unique answers per group. That's why we are using the Array#map method here. We transform the groups array into another array. Then, we want to find the unique values per group. Therefore, we can first join all answers per group. That leaves us with a long string like cedziyllnemy. We can then use the spread operator to split the string into an array where each item is a single character. These characters are then used to create a new set. The set removes any duplicates, and so we just have to return the size of the set.

Now, we have an array of numbers. Each number represents the count of unique answers per group. As a last step, we have to add those together, and our puzzle is solved. We can chain the Array#reduce method to our above code:

groups
  .map((group) => {
    const set = new Set([...group.join("")]);
    return set.size;
  })
  .reduce((previousValue, currentValue) => {
    return previousValue + currentValue;
  });

Now all unique answer counts per group (set size) are added together. The result is our puzzle solution. Tada!

For completeness, here is the full solution:

const groups = lines
  .join("\n")
  .split("\n\n")
  .map((group) => group.split("\n"));

return groups
  .map((group) => {
    const set = new Set([...group.join("")]);
    return set.size;
  })
  .reduce((previousValue, currentValue) => {
    return previousValue + currentValue;
  });

Part 2

Oof! Again we've misread something. We don't want to know if ANYONE in a group answered yes. We want to know if EVERYONE in a group answered yes to a specific question.

However, I've got good news for you. We can almost completely reuse our implementation from part 1.

First, let's create the groups array again like in part 1:

const groups = lines
  .join("\n")
  .split("\n\n")
  .map((group) => group.split("\n"));

Nice! If this is confusing you, look up the explanation in part 1. We already did this.

Now, again, we want to transform the group array into the answer counts. This time, however, we have to make sure that these answers were given by every person in a group. Therefore, we'll have to change our previous implementation a bit.

Remember, we used this:

groups
  .map((group) => {
    const set = new Set([...group.join("")]);
    return set.size;
  })

The problem is, this does not check if everyone in the group has given the answer. However, at least we know which answers where given at all. All values in set are the answers, ANYONE in this group has given. Now we can simply check, whether this answer was given by EVERYONE:

groups
  .map((group) => {
    const set = new Set([...group.join("")]);
    
    return [...set].filter((character) => {
      return group.every((person) => person.includes(character));
    }).length;
  })

So, again, we are creating our set. We did so like in part 1, so read up the explanation there, if necessary. Now our set contains every answer given by this group. We can filter out every answer that was not given by EVERYONE. Therefore, we'll use the spread operator to convert our set to an array. Then, we'll use the Array#filter method to filter out characters. Like in another day's puzzle, we use the Array#every method on the group array here. After filtering, we can use the length property and we know how many answers where given by EVERYONE.

Nice! We've collected all unique answers and then removed every answer that was not given by EVERY person of that group. The last thing to do is adding up the counts. This is done like in part 1:

groups
  .map((group) => {
    const set = new Set([...group.join("")]);

    return [...set].filter((character) => {
      return group.every((person) => person.includes(character));
    }).length;
  })
  .reduce((previousValue, currentValue) => {
    return previousValue + currentValue;
  });

That's it! We've solved the puzzle. Here's the full solution:

const groups = lines
  .join("\n")
  .split("\n\n")
  .map((group) => group.split("\n"));

return groups
  .map((group) => {
    const set = new Set([...group.join("")]);

    return [...set].filter((character) => {
      return group.every((person) => person.includes(character));
    }).length;
  })
  .reduce((previousValue, currentValue) => {
    return previousValue + currentValue;
  });

Conclusion

Today's puzzle required us to find a format that's easy to use. However, data split over several lines shouldn't be an issue anymore. Also, we had to find unique values. Therefore, I've shown you a way how to do it without external dependencies.

Thanks a lot for reading this post. Please consider sharing it with your friends and colleagues. See you tomorrow!

PS.: Here's a different approach for today's puzzle: https://twitter.com/kais_blog/status/1335597350283317254

If you like my content and you want to see more, please follow me on Twitter!
Questions, feedback or just wanna chat? Come and join my Discord!
All TypeScript Solutions for Advent of Code 2020

I'll try to update asap. Please bear in a mind that I'll probably lag one or multiple days behind.