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
For example:
1-3 a: abcde
1-3 b: cdefg
2-9 c: ccccccccc
One line contains a password policy and a password, so first I separated the policy from the password
const [policy, password] = line.split(': ');
Then I separated the numbers from the character in the policy:
const [amount, character] = policy.split(' ');
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('-');
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'];
I got a tip to make it sooo much more elegant:
let validChars = Array.from('0123456789abcdef');
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' ) { ... }
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) { ... }
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;
}
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;
}
Compare with
let currentChar = colourArray[i];
if (validChars.indexOf(currentChar) === -1) {
return false;
}
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
Top comments (13)
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 😄
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: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.Ah, clever! Thanks for the code example!
Awesome post! I'm also taking adventures at the Advent of Code this year and I think I can give some thoughts too:
Array.includes()
: returnstrue
orfalse
instead of the indexconst
on variables that do not change (consider capitalizing some of them too)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 solutionKeep coding!! 😄
Very good tips, thank you! I should've noticed
includes
and when I can useconst
. Actually, in my day 7 code (bags...) I useincludes
!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)
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!I don't know why but my first thought with the validation on day 4 was RegEx
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. 😄
Such luck: there is a post titled "How to use Regular Expressions in JavaScript" under Trending on DEV. Gotta check that out.
Regex is something yeah :D this site is great for testing out statements, also has useful tips :)
Thanks for the link!
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-[...]!
Destructuring........ facepalm for me there too. Would have saved time on that challenge.
Nice one, thanks.