Advent of code in SPL - 2025 day 3
- Gabriel Vasseur

- 19 hours ago
- 4 min read
Day 3 is here.
Part 1
This one isn't too difficult. Given a series of digits such as 818181911112111, we need to pick two digits (conserving the order) to make the highest 2-digit number possible, so in this example 92. The first digit is obviously the most important, so we always want the highest one available, but we need to reserve at least one digit at the end of the bank for the second digit.
So take the bank, remove the last digit, and find the highest digit:

Now for the second digit, we first need to figure out what options we have. How much of the bank is still available. For that we first need to find where digit1 is in bank:

And get the leftover usable bank which will be candidates for digit2:

The difficulty here is figuring if indexes start at 0 or 1, and it's not consistent between mvfind and substr! Now we can easily find digit2 and put things together:

What could go wrong? Maybe we should check that mvfind returns the first occurrence of digit1. So we check the docs: "If a match exists, the index of the first matching value is returned", sounds good. We can double check by testing with a different bank (notice the 2nd 9):

This solution should scale well, so we'll just apply this to the challenge data and sum it all up:

Done!
Part 2
This more of the same, now we need 12-digit joltages. So that means the first digit will be the highest digit in the bank once we removed the last 11 digits. Then whatever is left (should be at least 11 digits, but probably more), we need to do the same but this time reserving the last 10 digits. And so on.
Of course it would be nice to have a loop (we'll come back to that), but my first tendency was to just copy and paste. It's only 12 iterations. But 12 is still a lot to tweaks to do manually, so let's rewrite what we've done in a cleaner way:

Notice how we haven't really done anything here. We're just tweaking the approach we know works to put it in a shape that can be looped over. Let's make that more evident by figuring out how we could do the second digit with as much of the same code as possible. What difference do we need to introduce?

Not bad, we now have the exact same code for both iterations, the only difference being how many digits to reserve for the next step. Let's see if we can scale it up:

Ok that works, now it shouldn't be too painful to copy paste it the full 12 times:

Just add | stats sum(joltage) as joltage and we've got the answer we're looking for!
foreach
Now can we do better with foreach?
Foreach is supposed to iterate over a list of fields, but nothing says the fields have to exist. So why don't we iterate over fields named after the one thing that changes in our copy-pasted iterations:

Amazingly, that works!
foreach aside
I say amazingly because I was surprised. This tells us something about how the subsearch of foreach is put together: <<FIELD>> is expanded to the name of the field without assuming that it's actually a field. That's why it works. In eval syntax you can always put a field name in double quotes "<<FIELD>>" if you want to refer to the name of the field and in single quotes '<<FIELD>>' if you want to refer to the value of the field (its content). I took the habit of always using the right quotes just to make things clearer, but amazingly in this case we are better off not, and splunk doesn't have to know that we're not even iterating over actual fields. Of course it would make no sense using single quotes '<<FIELD>>', since for the fields we're iterating on do not exist. But if we put double quotes we get an error:

It's correctly using the name of the field, i.e. 11, but assuming it's a string of text and refusing to do maths with it. We could fix this with tonumber():

Unfortunately here we hit a pet peeve of mine with splunk: there's a number of eval functions that pointlessly refuse to work with hard-coded values and will only work with fields... so:

This works, but of course the initial version was more elegant. Sometimes I overthink things! This definitely taught me more about the nuances of foreach.
Another aside: foreach multivalue
In recentish versions of splunk, foreach has got a "multivalue" mode added to it. So instead of iterating over fields that have to be spelled out, it can iterate over the values of a single multivalue field.
Let's first build the multivalue field:

That looks good. Let's now introduce it to our search. You would think this works:

Hmm, thankfully eval commands can be grouped together and splunk will respect the order (the docs say "You can chain multiple eval expressions in one search using a comma to separate subsequent expressions. The search processes multiple eval expressions left-to-right and lets you reference previously evaluated fields in subsequent expressions.").
So let's do that (and also <<FIELD>> needs to be replaced with <<ITEM>> in this syntax):

We got there!
Which version do you prefer?



Comments