Advent of Code 2024

Advent of Code 2024

Welcome to Advent of Code 2024!

Like every year, I start the challenge with the best attitude and love of being an Elixir programmer. Although I know that at some point, I will go to the “what is this? I hate it” phase, unlike other years, this time, I am committed to finishing Advent of Code and, more importantly, sharing it with you.

I hope you enjoy this series of December posts, where we will discuss the approach for each exercise. But remember that it is not the only one, and the idea of ​​this initiative is to have a great time and share knowledge, so don’t forget to post your solutions and comments and tag us to continue the conversation.

Let’s go for it!

Day 1: Historian Hysteria

Before starting any exercise, I suggest spending some time defining the structure that best fits the problem’s needs. If the structure is adequate, it will be easy to reuse it for the second part without further complications.

In this case, the exercise itself describes lists as the input, so we can skip that step and instead consider which functions of the Enum or List modules can be helpful.

We have this example input:

3   4

4   3

2   5     

1   3   

3   9

3   3

The goal is to transform it into two separate lists and apply sorting, comparison, etc.

List 1: [3, 4, 2, 1, 3, 3 ] 

List 2: [4, 3, 5, 3, 9, 3]

Let’s define a function that reads a file with the input. Each line will initially be represented by a string, so use String.split to separate it at each line break. 


 def get_input(path) do
   path
   |> File.read!()
   |> String.split("\n", trim: true)
 end


["3   4", "4   3", "2   5", "1   3", "3   9", "3   3"]

We will still have each row represented by a string, but we can now modify this using the functions in the Enum module. Notice that the whitespace between characters is constant, and the pattern is that the first element should go into list one and the second element into list two. Use Enum.reduce to map the elements to the corresponding list and get the following output:


%{
 first_list: [3, 3, 1, 2, 4, 3],
 second_list: [3, 9, 3, 5, 3, 4]
}

I’m using a map so that we can identify the lists and everything is clear. The function that creates them is as follows:

 @doc """
 This function takes a list where the elements are strings with two
 components separated by whitespace.


 Example: "3   4"


 It assigns the first element to list one and the second to list two,
 assuming both are numbers.
 """
 def define_separated_lists(input) do
   Enum.reduce(input, %{first_list: [], second_list: []}, fn row, map_with_lists ->
     [elem_first_list, elem_second_list] = String.split(row, "   ")


     %{
       first_list: [String.to_integer(elem_first_list) | map_with_lists.first_list],
       second_list: [String.to_integer(elem_second_list) | map_with_lists.second_list]
     }
   end)
 end

Once we have this format, we can move on to the first part of the exercise.

Part 1

Use Enum.sort to sort the lists ascendingly and pass them to the Enum.zip_with function that will calculate the distance between the elements of both. Note that we are using abs to avoid negative values, and finally, Enum.reduce to sum all the distances.

first_sorted_list = Enum.sort(first_list)
   second_sorted_list = Enum.sort(second_list)


   first_sorted_list
   |> Enum.zip_with(second_sorted_list, fn x, y -> abs(x-y) end)
   |> Enum.reduce(0, fn distance, acc -> distance + acc end)

Part 2

For the second part, you don’t need to sort the lists; use Enum.frequencies and Enum.reduce to get the multiplication of the elements.

 frequencies_second_list = Enum.frequencies(second_list)


   Enum.reduce(first_list, 0, fn elem, acc ->
     elem * Map.get(frequencies_second_list, elem, 0) + acc
   end)

That’s it. As you can see, once we have a good structure, the corresponding module, in this case, Enum, makes the operations more straightforward, so it’s worth spending some time defining which input will make our life easier.

You can see the full version of the exercise here.

Keep reading

Optimising for Concurrency: Comparing and contrasting the BEAM and JVM virtual machines

Optimising for Concurrency: Comparing and contrasting the BEAM and JVM virtual machines

Attila Sragli explores the BEAM VM's inner workings, comparing them to the JVM to highlight their importance.

MongooseIM 6.3: Prometheus, CockroachDB and more

MongooseIM 6.3: Prometheus, CockroachDB and more

Pawel Chrząszcz introduces MongooseIM 6.3.0 with Prometheus monitoring and CockroachDB support for greater scalability and flexibility.

Why you should consider machine learning for business
thumbnail image of machine learning for business

Why you should consider machine learning for business

Here's how machine learning drives business efficiency, from customer insights to fraud detection, powering smarter, faster decisions.