Day 6: Trash Compactor
by @scarf005
Puzzle description
https://adventofcode.com/2025/day/6
Solution Summary
Processing data row-by-row is usually straightforward, but handling columns can be tricky.
Here comes transpose to the rescue!
The transpose method switches the rows and columns of a 2D collection, which is exactly what we need for this puzzle.
Part 1
123 328 51 64
45 64 387 23
6 98 215 314
* + * +
To make working with columns easier, we'd like the inputs to be like this:
123 45 6 *
328 64 98 +
51 387 215 *
64 23 314 +
First, let's split the input into a 2D grid:
def part1(input: String) = input.linesIterator.toVector // split input into lines
.map(_.trim.split(raw"\s+")) // split lines into words by whitespaces
// Vector(
// Array(123, 328, 51, 64),
// Array(45, 64, 387, 23),
// Array(6, 98, 215, 314),
// Array(*, +, *, +)
// )
After we transpose it, we get the desired output:
def part1(input: String) = input.linesIterator.toVector
.map(_.trim.split(raw"\s+"))
.transpose
// Vector(
// Vector(123, 45, 6, *),
// Vector(328, 64, 98, +),
// Vector(51, 387, 215, *),
// Vector(64, 23, 314, +)
// )
Now it's a matter of processing each column, which is fairly straightforward. Let's define an extension method to improve readability.
extension (xs: Iterator[(symbol: String, nums: IterableOnce[String])])
def calculate: Long = xs.iterator.collect {
case ("*", nums) => nums.iterator.map(_.toLong).product
case ("+", nums) => nums.iterator.map(_.toLong).sum
}.sum
Finally:
def part1(input: String): Long = input.linesIterator.toVector
.map(_.trim.split(raw"\s+"))
.transpose
.iterator
.map { col => (col.last, col.init) }
.calculate
Part 2
This time it's tricky, but what if we transpose the entire input string?
123 328 51 64
45 64 387 23
6 98 215 314
* + * +
would become
1 *
24
356
369+
248
8
32*
581
175
623+
431
4
Which is exactly what we need to calculate cephalopod math!
def part2(input: String) =
val lines = input.linesIterator.toVector // get list of lines
val ops = lines.last.split(raw"\s+").iterator // we'll use them later
lines
.init // transposing requires all rows to be of equal length, so remove symbols from last line for simplicity
.transpose
// Vector(
// Vector(1, , ),
// Vector(2, 4, ),
// Vector(3, 5, 6),
// Vector( , , ),
// Vector(3, 6, 9),
// Vector(2, 4, 8),
// Vector(8, , ),
// Vector( , , ),
// Vector( , 3, 2),
// Vector(5, 8, 1),
// Vector(1, 7, 5),
// Vector( , , ),
// Vector(6, 2, 3),
// Vector(4, 3, 1),
// Vector( , , 4)
// )
Now we can easily convert each column into cephalopod number strings:
def part2(input: String) =
val lines = input.linesIterator.toVector // get list of lines
val ops = lines.last.split(raw"\s+").iterator // we'll use them later
lines.init.transpose.map(_.mkString.trim)
// Vector(
// "1",
// "24",
// "356",
// "",
// "369",
// "248",
// "8",
// "",
// "32",
// "581",
// "175",
// "",
// "623",
// "431",
// "4",
// )
The only thing left is to split this Vector by separator "". Sadly, the scala
standard library doesn't have an Iterable.splitBy method (yet!), so we'll define our own:
extension [A](xs: IterableOnce[A])
// we're using Vector.newBuilder to build the result efficiently
inline def splitBy(sep: A) =
val (b, cur) = (Vector.newBuilder[Vector[A]], Vector.newBuilder[A]) // b stores the result, cur stores the current chunk
for e <- xs.iterator do
if e != sep then cur += e // if current element is not the separator, add it to the current chunk
else { b += cur.result(); cur.clear() } // else, append the current chunk to result and clear it
(b += cur.result()).result() // finally, append the last chunk and return the result
def part2(input: String) =
val lines = input.linesIterator.toVector // get list of lines
val ops = lines.last.split(raw"\s+").iterator // we'll use them later
lines.init.transpose.map(_.mkString.trim).splitBy("")
// Vector(
// Vector(1, 24, 356),
// Vector(369, 248, 8),
// Vector(32, 581, 175),
// Vector(623, 431, 4)
// )
Reusing the calculate extension method from part 1, we can now finish part 2:
def part2(input: String): Long =
val lines = input.linesIterator.toVector
val ops = lines.last.split(raw"\s+").iterator
val xss = lines.init.transpose.map(_.mkString.trim).splitBy("")
(ops zip xss).calculate // zip the operations with the chunks
Final Code
extension [A](xs: IterableOnce[A])
inline def splitBy(sep: A) =
val (b, cur) = (Vector.newBuilder[Vector[A]], Vector.newBuilder[A])
for e <- xs.iterator do
if e != sep then cur += e else { b += cur.result(); cur.clear() }
(b += cur.result()).result()
extension (xs: Iterator[(symbol: String, nums: IterableOnce[String])])
def calculate: Long = xs.iterator.collect {
case ("*", nums) => nums.iterator.map(_.toLong).product
case ("+", nums) => nums.iterator.map(_.toLong).sum
}.sum
def part1(input: String): Long = input.linesIterator.toVector
.map(_.trim.split(raw"\s+"))
.transpose
.iterator
.map { col => (col.last, col.view.init) }
.calculate
def part2(input: String): Long =
val lines = input.linesIterator.toVector
val ops = lines.last.split(raw"\s+").iterator
val xss = lines.init.transpose.map(_.mkString.trim).splitBy("")
(ops zip xss).calculate
Solutions from the community
- Solution by Jan Boerman
- Solution by Antoine Amiguet
- Solution by nichobi
- Solution by Guillaume Vandecasteele
- Solution by Raphaël Marbeck
- Solution by Henryk Česnolovič
- Solution by Philippus Baalman
- Solution by Yann Moisan
- Solution by counter2015
- Solution by Paweł Cembaluk
Share your solution to the Scala community by editing this page. You can even write the whole article! Go here to volunteer