0 Comments 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#

    .Select(m => m.Value)

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;
    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 More Effective LINQ. I'm also speaking on LINQ at Techorama Netherlands 2018 on 3rd October, so I'd love to see you there if you can make it.
Vote on HN