day 6
i really liked this one…
input parsing my beloved
click to view my solution
given the sample input:
123 328 51 64
45 64 387 23
6 98 215 314
* + * + assuming input is the above as one big string,
part 1
maths = input
|> Kino.Input.read()
|> String.split("\n")
|> Enum.map(fn line ->
line
|> String.split(" ")
|> Enum.filter(&(&1 != ""))
|> Enum.map(fn val ->
case Integer.parse(val) do
{val, _} -> val
:error -> String.to_atom(val)
end
end)
end)
|> Enum.zip()
|> Enum.map(&(Tuple.to_list(&1)))input parsing! hell yeah!
this spits out:
[
[123, 45, 6, :*],
[328, 64, 98, :+],
[51, 387, 215, :*],
[64, 23, 314, :+]
]the first chunk here turns things into nice little chunks:
...
|> String.split("\n")
|> Enum.map(fn line ->
line
|> String.split(" ")
end)this spits out out this
[
["123", "328", "", "51", "64", ""],
["", "45", "64", "", "387", "23", ""],
["", "", "6", "98", "", "215", "314"],
["*", "", "", "+", "", "", "*", "", "", "+", "", ""]
]then we filter out empty strings, and parse said non empty strings
...
|> Enum.filter(&(&1 != ""))
|> Enum.map(fn val ->
case Integer.parse(val) do
{val, _} -> val
:error -> String.to_atom(val)
end
end)we attempt to parse as integer, if we fail, we know its a + or *, so
we cast that to an atom, and get :+ / :*
this gets us:
[
[123, 328, 51, 64],
[45, 64, 387, 23],
[6, 98, 215, 314],
[:*, :+, :*, :+]
]this is great, buts its the wrong way!, so we Enum.zip, and Tupleify the results of that
|> Enum.zip()
|> Enum.map(&(Tuple.to_list(&1)))[
[123, 45, 6, :*],
[328, 64, 98, :+],
...
]nice
maths
|> Enum.map(fn math ->
{op, nums} = List.pop_at(math, length(math) - 1)
Enum.reduce(nums, fn num, acc ->
apply(Kernel, op, [num, acc])
end)
end)
|> Enum.sum()and then yeah, we take each list, pop the last value (the operation), and reduce down the list of numbers using the operation to a single value and sum all of the math
my fav thing above is:
apply(Kernel, op, [num, acc])apply is very useful, it allows you to invoke a function on a module,
now Kernel in elixir is the sort of ‘root’ namespace everything lives on,
which includes multiplication and addition, so lets say we’re accumulated 10, and our num is
5, and our op is :+, we’d invoke:
apply(Kernel, :+, [5, 10])and this would work!
part 2
proper_maths = input
|> Kino.Input.read()
|> String.split("\n")
|> Enum.map(&(String.graphemes(&1)))
|> Enum.zip()
|> Enum.map(fn tuple ->
tuple
|> Tuple.to_list()
|> Enum.filter(&(&1 !== " "))
|> Enum.map(fn val ->
case Integer.parse(val) do
{val, _} -> val
:error -> String.to_atom(val)
end
end)
end)
|> Enum.chunk_by(&(&1 == []))
|> Enum.take_every(2)output:
the
~c"\b"bit, is because lists of certain bytes sometimes are read as ascii in elixir/erlang…you can configure it not to do that, which i usually do, but i felt like leaving it in there to show this oddity
[
[[1, :*], [2, 4], [3, 5, 6]],
[[3, 6, 9, :+], [2, 4, 8], ~c"\b"],
[[3, 2, :*], [5, 8, 1], [1, 7, 5]],
[[6, 2, 3, :+], [4, 3, 1], [4]]
]how i approaches this, was realizing that i could Enum.zip the output of String.graphemes:
proper_maths = input
|> Kino.Input.read()
|> String.split("\n")
|> Enum.map(&(String.graphemes(&1)))which gives me
[
["1", "2", "3", " ", "3", "2", "8", " ", " ", "5", "1", " ", "6", "4", " "],
[" ", "4", "5", " ", "6", "4", " ", " ", "3", "8", "7", " ", "2", "3", " "],
[" ", " ", "6", " ", "9", "8", " ", " ", "2", "1", "5", " ", "3", "1", "4"],
["*", " ", " ", " ", "+", " ", " ", " ", "*", " ", " ", " ", "+", " ", " "]
]in this case, i actually like the empty space characters!
when you Enum.zip that, you get:
[
{"1", " ", " ", "*"},
{"2", "4", " ", " "},
{"3", "5", "6", " "},
{" ", " ", " ", " "},
{"3", "6", "9", "+"},
{"2", "4", "8", " "},
...
]nice! so the sections of numbers are seperated by {" ", " ", " ", " "},
and the first numbers last row is going to be the ‘operator’
now lets map over that and clean it up a bit:
|> Enum.map(fn tuple ->
tuple
|> Tuple.to_list()
|> Enum.filter(&(&1 !== " "))
|> Enum.map(fn val ->
case Integer.parse(val) do
{val, _} -> val
:error -> String.to_atom(val)
end
end)
end)we get this:
[
[1, :*],
[2, 4],
[3, 5, 6],
[],
[3, 6, 9, :+],
[2, 4, 8],
...
]so each time we see an empty list, we should start a new ‘chunk’
then we do so:
...
|> Enum.chunk_by(&(&1 == []))
|> Enum.take_every(2)(take every is because we still get some empty lists, which we want to scrape away)
and then yeah, parsed:
[
[[1, :*], [2, 4], [3, 5, 6]],
[[3, 6, 9, :+], [2, 4, 8], ...]
...
]so then we loop over these new ‘problems’, and pop the
first list out to get its first item, modify it so we remove the :op,
and then basically do the same thing before (except using Integer.undigits
as i saw mudkip using the other day to turn out list of numbers into an
actual digit)
proper_maths
|> Enum.map(fn math ->
[first | rest] = math
{op, first} = List.pop_at(first, length(first)-1)
nums = [first | rest] |> Enum.map(&(Integer.undigits(&1)))
Enum.reduce(nums, fn num, acc ->
apply(Kernel, op, [num, acc])
end)
end)
|> Enum.sum()tidy!
the full solution can be found [here]
four solutions to ramble on today…
however i know why this is
a combination of flying in late, and some folks having a work christmas party at a brewery…
less work for me, more focus for those that did to todays by the night of the day!
others
[terales/aoc-elixir] elixir[nint8835/advent-of-code] f#clever trick here to reverse the list so the operator is the first thing rather than the last thing in the list!
some interesting code to do zipping with padding and such, grabbing the ‘longest’ enumerable to use here is clever as well
i like this solution
[ncashin/aoc2025] elixirextracting the operators before you dig into the problem is clever:
let operators = inputData |> Array.map ( String.split " " >> Array.filter (String.IsNullOrEmpty >> not) ) |> Array.last |> Array.map (function | "+" -> (+) | "*" -> (*) | _ -> failwith "Invalid operator")i like the mapping of string values to just
+and*
transposebeing used here, i assume similar to thezipmyself and alex pulled outthe evaluation being a zip with the operators is cool
let evaluateNumbers (numberArray: int64[][]) : int64 = numberArray |> Array.zip operators |> Array.sumBy (fun (op, numbers) -> numbers |> Array.reduce op)but wait, you have a
zipso atransposeisn’t azip?oh wait! it looks like the
Enum.zip_with(&1)from earlier in alexes solution was the same sort of thing, its not just zipping two things together, butnthings together, cool
[djrideout/advent2025] rustnatalie is here!!! hello natalie!!!
she starts out with
defmodule Main do def transpose(rows) do rows |> Enum.zip() |> Enum.map(&Tuple.to_list/1) end endoh god its sorting the input beforehand all over again
gonna include your entire part 2 in one chunk, because its nice
problems = input |> Kino.Input.read() |> String.split("\n", trim: true) |> Enum.map(&String.graphemes/1) |> then(&Enum.zip(&1)) |> Enum.map(fn tuple -> tuple |> Tuple.to_list() |> Enum.join() end) |> Enum.chunk_by(fn a -> a |> String.trim() |> String.length() == 0 end) |> Enum.filter(fn a -> a |> Enum.at(0) |> String.trim() |> String.length() != 0 end) problems |> Enum.map(fn problem -> operator = problem |> Enum.at(0) |> String.last() problem |> Enum.map(&String.replace(&1, ~r/[^0-9]/, "")) |> Enum.map(&String.to_integer(&1)) |> Enum.reduce(fn value, acc -> apply(Kernel, operator |> String.to_atom(), [value, acc]) end) end) |> Enum.sum()nice, cool seeing people get good use out of an inline
thenin a pipelinealso lets go
apply(Kernel, operator, ...)
regex being pulled out here for data parsing, sensible!
very parsing heavy, and no
zipping of any kind, the odd one out todaybut still an epic solution nonetheless
godspeed to the soldiers we lost today, for they shall return tomorrow for day 7 i am sure…
also… this is the halfway mark! 1/2 the way there! lets go!
any thoughts about any of the above?
reach out: