Posted in:

Day 12’s Advent of Code challenge involved navigating through a huge JSON object and adding up all the numeric values. You could actually ‘cheat’ for part a by just using a regex, but for part b, we need to dig out the Newtonsoft.Json library and do some recursion. Here’s me describing my solutions:

So here’s the part a solution with Regex in C#

Regex.Matches(
File.ReadAllText("day12.txt"), 
    @"[\-0-9]+")
    .Cast<Match>()
    .Select(m => m.Value)
    .Select(int.Parse)
    .Sum()

And part b, using Newtonsoft.Json, and an extension method:

void Main()
{
    var json = File.ReadAllText("day12.txt");
    var o = JObject.Parse(json);
    SumTokens(o).Dump(); // 65402
}

long SumTokens(JToken token)
{
    if (token is JObject)
    {    
        var jo = (JObject)token;
        if (jo.IsRed()) return 0;
        return jo.Properties().Select(p => p.Value).Sum(jt => SumTokens(jt));
    }
    else if (token is JArray)
    {
        var ja = (JArray)token;
        return ja.Sum(jt => SumTokens(jt));
    }
    else if (token is JValue)
    {
        var jv = (JValue)token;
        return (jv.Value is long) ? (long)jv.Value : 0;
    }
    token.Dump();
    throw new InvalidOperationException();
}

static class MyExtensions
{
    public static bool IsRed(this JObject jobject)
    {
        return jobject.Properties()
            .Select(p => p.Value).OfType<JValue>()
            .Select(j => j.Value).OfType<string>()
            .Any(j => j == "red");
    }
}

Finally, once again I’ve been trying to improve my F# skills and today I made use of pattern matching on object type with the :? operator, as well as casting with the :?> operator.

let json = File.ReadAllText("day12.txt")
let o = JObject.Parse(json)

let shouldAvoid avoid (jo:JObject) = 
    jo.Properties() |> Seq.exists (fun p -> match p.Value with | :? JValue as jv -> jv.Value = avoid | _ -> false)

let rec getSum avoid (token:JToken) =
    match token with
    | :? JObject as jo -> 
        if shouldAvoid avoid jo then 0L
        else jo.Properties() |> Seq.map (fun p -> p.Value) |> Seq.map (getSum avoid) |> Seq.sum 
    | :? JArray as ja -> ja |> Seq.cast<JToken> |> Seq.map (getSum avoid) |> Seq.sum 
    | :? JValue as jv -> if jv.Type = JTokenType.Integer then jv.Value :?> int64 else 0L
    | _ -> failwith (sprintf "unknown token %A" (token.GetType())  )

getSum null o |> printfn "a: %d" // 111754
getSum "red" o |> printfn "b: %d" // 65402
Want to learn more about LINQ? Be sure to check out my Pluralsight course LINQ Best Practices.