DEV Community

loading...
Cover image for What I've learned from the Advent of Code so far (days 1-5)
Ompeluseura LevelUP Koodarit

What I've learned from the Advent of Code so far (days 1-5)

minna_xd profile image Minna Nurmiluoto Updated on ・5 min read

I consider myself an advanced beginner in programming. I lack a lot of knowledge in best practices, gotchas, elegance... let alone algorithms, optimizations... I have no clue.

I'm quite sure I won't make it through Advent of Code but I wanted to give it a go anyway. I first thought to use Java to solve the puzzles because I'm more comfortable processing line-by-line input and doing "tricks" with it (I did a comprehensive course on it just at the beginning of this year), but decided on JavaScript because it's more beneficial for the things I'm learning at the moment.

On the first five days, I had a couple of facepalm moments 🤦 but also some proud moments 🏆.

Here are some of the things that have helped me on puzzle-solving days 1-5.

Neat feature: Destructuring

On day 2 I was quite proud of myself for remembering the destructuring assignment feature. The task is to process a list with the following data:

int-int char: string
Enter fullscreen mode Exit fullscreen mode

For example:

1-3 a: abcde
1-3 b: cdefg
2-9 c: ccccccccc
Enter fullscreen mode Exit fullscreen mode

One line contains a password policy and a password, so first I separated the policy from the password

const [policy, password] = line.split(': ');
Enter fullscreen mode Exit fullscreen mode

Then I separated the numbers from the character in the policy:

const [amount, character] = policy.split(' ');
Enter fullscreen mode Exit fullscreen mode

And finally the first number and the second number (representing min and max values in the first part of the puzzle and two positions in the second part):

const [min, max] = amount.split('-');
Enter fullscreen mode Exit fullscreen mode

Very handy!

Neat method: Array.from()

For the colour code validation on day 4, I use indexOf(). First I had an array with the possible values like so:

let validChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
Enter fullscreen mode Exit fullscreen mode

I got a tip to make it sooo much more elegant:

let validChars = Array.from('0123456789abcdef');
Enter fullscreen mode Exit fullscreen mode

Coolio! 😎 As it sounds like, here Array.from() creates an array from the given string.

If you are wondering why I'm processing the numbers as strings, it's just so much simpler because the valid characters are either numbers or strings. And actually, the value comes as a string to validation so === works more reliably this way.

I'm really digging this array of valid values, too. First I had

if (value === 'amb' || 
    value === 'blu' || 
    value === 'brn' || 
    value === 'gry' || 
    value === 'grn' || 
    value === 'hzl' || 
    value === 'oth' ) { ... }
Enter fullscreen mode Exit fullscreen mode

for the hair colour validation 😅 but I just changed it to

let validColors = ['amb', 'blu', 'brn', 'gry', 'grn', 'hzl', 'oth'];

if (validColors.indexOf(value) != -1) { ... }
Enter fullscreen mode Exit fullscreen mode

Tip: Break up processing into functions

On day 4 you have to do data validation and I was puzzled by how I'd be able to end processing of an invalid value in a nested loop and get back to the outer loop to validate the next value. I got a tip – one that I should remember by now – that I should make more helper functions. There's no such thing as too many functions (within reason). 😄

My colour code validation was made much simpler with a helper function that returns either true or false.

function hexValidity(hexValue) {
  let validChars = Array.from('0123456789abcdef');
  let colourArray = Array.from(hexValue);

  if (colourArray[0] != '#' || colourArray.length != 7) {
    return false;
  }

  for (let i = 1; i < colourArray.length; i++) {
    let currentChar = colourArray[i];

    if (validChars.indexOf(currentChar) === -1) {
      return false;
    }
  }
  return true;
}
Enter fullscreen mode Exit fullscreen mode

Tip: Create variables more often

The code is easier to read when you first assign results of functions, values from arrays, etc. in variables and use them in another structure. For example in my colour validation code for day 4, I first had:

if (validChars.indexOf(colourArray[i]) === -1) {
  return false;
}
Enter fullscreen mode Exit fullscreen mode

Compare with

let currentChar = colourArray[i];

if (validChars.indexOf(currentChar) === -1) {
  return false;
}
Enter fullscreen mode Exit fullscreen mode

Tip: Use modulo where you can

I keep forgetting how useful (and multi-use) modulo % is.

For my toboggan trajectory on day 3, I skip to the beginning of the line ("horizontal index" 0) when I go over the length of the array (31) so in my code, I subtract the length from the horizontal index if it's over 30 (last possible index). With modulo, I could just use index % 31 and be done with it.

If you have a situation where a value has to loop back to 0 at some point, use modulo.

Best practice: Early exit

It's best to start by validating your data so you can break out of a loop/function as early as possible. For example on day 4, it's wise to check if the passport ID even has the required 9 characters before you start validating if each of the characters is a digit. Same with the hex colour codes: if it doesn't have a hash # at the beginning and exactly 6 characters after it, there's no point validating it in more detail.

Take heed: Scope of variables

This was a moment of a huge facepalm. On day 4 you have to do data validation, which in itself is quite complicated to do for seven different value types.

After I'd extracted the value validation itself into a separate function, as mentioned above, I found myself facing an infinite loop. The code was able to process the first three values ok but then it got stuck looping with second and third value. A lot of debugging later, I was this much wiser: 💡 remember to always declare the initial variable of a for loop 💡 or the code may end up using a completely wrong variable.

I had forgotten the let from a couple of for loops where used i as the index counter. 🤦

This actually brings to mind another tip for myself: keep in mind the existence of for/of loop! I could've made my code a lot simpler with it.

A proud moment on day 3

First I was completely at a loss with the puzzle on day 3: how am I supposed to figure out a trajectory through lines of data? I don't know about vectors or any map algorithms.

I started visualizing the problem as a matrix, but then was unsure how that would be done in JavaScript (would've been easy-peasy in Java) but it got me a step further: I put the lines into an array (array item per line) for vertical movement and used charAt for the horizontal dimension of my "matrix". Once I had my function for the part 1 working and I was looking at part 2, I first thought "oh no, the function is going to be so messy with the for loop times 5". But then I realized, if I refactor my first function a bit I can reuse it by giving the numbers for traversing (x steps right, y steps down) as parameters and just assign the results to variables. 🏆

Cover photo by Markus Spiske on Unsplash

Discussion (13)

pic
Editor guide
Collapse
matrixx profile image
Saija Saarenpää

Great post! I haven't had time to participate in the Advent of Code, but I still occasionally enjoy reading from others doing the tasks. I have one suggestion. Not necessarily an improvement, but an alternative way for the for loop for checking the hex code validity. This is also using the includes Ilê Caian already mention about:

function hexValidity(hexValue) {
  let validChars = Array.from('0123456789abcdef');
  let colourArray = Array.from(hexValue);

  if (colourArray[0] != '#' || colourArray.length != 7) {
    return false;
  }

  return colourArray.slice(1).every(character => {
    return validChars.includes(character);
  });
}
Enter fullscreen mode Exit fullscreen mode

The slice function gives a sub array starting from the index 1, so it will omit the # sign. every function for the array iterates over every character of the array and returns true if all of the iterations return true. In case any of the iterations return false, the whole function returns false.

Collapse
minna_xd profile image
Minna Nurmiluoto Author

Ah, clever! Thanks for the code example!

Collapse
caiangums profile image
Ilê Caian

Awesome post! I'm also taking adventures at the Advent of Code this year and I think I can give some thoughts too:

  • use of Array.includes(): returns true or false instead of the index
let validColors = ['amb', 'blu', 'brn', 'gry', 'grn', 'hzl', 'oth'];

if (validColors.includes(value)) { ... }
Enter fullscreen mode Exit fullscreen mode
  • you can "empty-split" strings
const validChars = '0123456789abcdef'.split();
Enter fullscreen mode Exit fullscreen mode
  • make use of const on variables that do not change (consider capitalizing some of them too)
const VALID_CHARS = '0123456789abcdef'.split();

if (VALID_CHARS.includes(letter)) { ... }
Enter fullscreen mode Exit fullscreen mode

I would also suggest using some of RegExp here and there. You can make good use of .match() and .replace() where you need to find patterns in repetitive strings. As an example, you can check the usage of .replace() at this solution

Keep coding!! 😄

Collapse
minna_xd profile image
Minna Nurmiluoto Author

Very good tips, thank you! I should've noticed includes and when I can use const. Actually, in my day 7 code (bags...) I use includes!

Thank you also for that code sample! I didn't know you can assign the groups into variables like that. 👍

A question which is either "hey, I'm able to give you a suggestion too!" OR there's some optimization magic going on that I don't know about. About the regex, you have:

/(\d*)-(\d*) (\w): (\w*)/g,

Is there a reason why you use * instead of +? Why not:

/(\d)-(\d) (\w): (\w+)/g,

(or \d+ in case the numbers are double or more digits)

Collapse
caiangums profile image
Ilê Caian

Thanks for noticing it! It's true that I could've used + instead of *!

About maintaining \d, I didn't see if the file has values with more than 1 digit, but with * or + will cover it! 😄

It's just a habit using * instead of + and maybe it could be optimized as you said!

Collapse
annisalli profile image
Anniina Sallinen

Very nice post, sounds like you have learned a lot! The destructuring is available also in Python and Clojure, but I tend to forget it all the time 🤦‍♀️ For example when solving the Advent of Code puzzles 😄

Collapse
syentix profile image
syentix

I don't know why but my first thought with the validation on day 4 was RegEx

Collapse
minna_xd profile image
Minna Nurmiluoto Author

Oh yes, I would've done regex but I didn't know how. 🙈 I'd come across regex in a search and replace usage only. I've improved a lot of my code "retroactively" so I'll probably look into regex solution as well because I like regex. 😄

Collapse
minna_xd profile image
Minna Nurmiluoto Author

Such luck: there is a post titled "How to use Regular Expressions in JavaScript" under Trending on DEV. Gotta check that out.

Thread Thread
syentix profile image
syentix

Regex is something yeah :D this site is great for testing out statements, also has useful tips :)

Thread Thread
minna_xd profile image
Minna Nurmiluoto Author

Thanks for the link!

Thread Thread
lucasqueiroz profile image
Lucas Queiroz

RegexEr is the best!
I highly recommend learning Regex, it will be very handy one day or another! And I don't even mean the more advanced features, just the basics would be a huge step forward!
You did an awesome job on days 1-5. Hoping to hear again on 6-[...]!

Collapse
calvin087 profile image
Calvin T

Destructuring........ facepalm for me there too. Would have saved time on that challenge.

Nice one, thanks.