Back Original

Advent of Code 2025 Day 5 in Functionalish C#

DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

In this post we'll walk through my solution to Advent of Code 2025 Day 5 in C#.

Part 1

Notes:

Part 1: 
* Fresh food ranges, blank line, then inventory food

Goal: 
* Get the count of fresh foods

Subproblems: 
* A: How to parse the data
    * Find first empty line - split on that
    * Grab the fresh data and put into set
    * Go over other food and see if fresh
* B: How to do the fresh inventory parsing - O(n)
    * 1: Simple fresh to set then go thorugh inventory and check

-> Hashset not working, it's OOM
* To do less memory: 
    * A: Do intervals
        * Sort by low ranges, merge ranges
        * See if fresh food id in the inventories

I originally tried doing a simple range to set lookup but OOMed as the number values are huge. So had to switch to a merge range and binary search.

public static long Day5Part1(string[] lines)
{
    var indexOfInventory = lines.ToList().IndexOf("");
    var foodInventory = lines.Skip(indexOfInventory + 1).ToList();
    var freshFoodRanges = BuildFreshFoodDataRanges(lines.Take(indexOfInventory).ToArray());

    return foodInventory
        .Select(long.Parse)
        .Select(foodId => IsFoodIdInFreshData(freshFoodRanges, foodId))
        .Count(isInRange => isInRange);
}

public static List<FoodRangeInclusive> BuildFreshFoodDataRanges(string[] lines)
{
    return lines
        .Select(l => l.Split("-"))
        .Select(rawStrings => new FoodRangeInclusive(long.Parse(rawStrings[0]), long.Parse(rawStrings[1])))
        .OrderBy(r => r.Low)
        .Aggregate(new List<FoodRangeInclusive>(), (acc, next) =>
        {
            if (acc.Count == 0 || next.Low > acc[^1].High + 1)
                acc.Add(next);
            else
                acc[^1] = new FoodRangeInclusive(acc[^1].Low, Math.Max(acc[^1].High, next.High));
            return acc;
        });
}

public static bool IsFoodIdInFreshData(List<FoodRangeInclusive> freshFoodRanges, long foodId)
{
    int lo = 0, hi = freshFoodRanges.Count - 1;

    while (lo <= hi)
    {
        int mid = lo + (hi - lo) / 2;
        var range = freshFoodRanges[mid];

        if (foodId < range.Low)
            hi = mid - 1;
        else if (foodId > range.High)
            lo = mid + 1;
        else
            return true; // foodId is within [Low, High]
    }

    return false;
}

// Doesn't work - OOM, numbers too big
// public static HashSet<long> BuildFreshDataSet(string[] lines)
// {
//     return lines 
//         .Select(l => l.Split("-"))
//         .Select(rawStrings => (Lower: long.Parse(rawStrings[0]), Upper: long.Parse(rawStrings[1])))
//         .SelectMany(t => EnumerableExtensions.RangeInclusive(t.Lower, t.Upper).ToList())
//         .ToHashSet();
// }

Part 2

Part 2 was pretty easy cause I already had the functionality coded - just had to sum up the merged ranges.

public static long Day5Part2(string[] lines)
{
    var indexOfInventory = lines.ToList().IndexOf("");
    var foodInventory = lines.Skip(indexOfInventory + 1).ToList();
    var freshFoodRanges = BuildFreshFoodDataRanges(lines.Take(indexOfInventory).ToArray());

    return freshFoodRanges
        .Sum(range => range.High - range.Low + 1);
}

Next

HAMINIONs Members get access to the full source code (Github Repo) and that of dozens of other example projects.

If you liked this post you might also like: