<jupyter_start><jupyter_text>Advent of Code 2015 Day 12: JSAbacusFramework.iohttps://adventofcode.com/2015/day/12Santa's Accounting-Elves need help balancing the books after a recent order. Unfortunately, their accounting software uses a peculiar storage format.They have a JSON document which contains a variety of things: arrays (`[1,2,3]`), objects (`{"a":1, "b":2}`), numbers, and strings. Your first job is to simply find all of the numbers throughout the document and add them together.You will not encounter any strings containing numbers.<jupyter_code>(require racket)<jupyter_output><empty_output><jupyter_text>The first thing we need to do is load up our test data into a string. Racket has a handy function for this, `file->lines`, but as our data is a single line, we need to pick the first entry in the list that it returns.<jupyter_code>(define data-str (first (file->lines "data/input12.data")))<jupyter_output><empty_output><jupyter_text>We don't even need to parse the json. As we're told that no strings contain numbers, we can simply grab all the numbers with a regular expression. A simple regex for numbers we can use is `/-?\d+/`, meaning an optional dash followed by 1 or more digits. In Racket we write this as```scheme(regexp-match* px"-?\\d+" data-str)```noting that we need to escape the backslash. Let's examine the first few values we get from that:<jupyter_code>(take (regexp-match* #px"-?\\d+" data-str) 10)<jupyter_output><empty_output><jupyter_text>Ok, that captures the right bits, but if we're to sum them up we need to convert the strings to actual numbers:<jupyter_code>(take (map string->number (regexp-match* #px"-?\\d+" data-str)) 10)<jupyter_output><empty_output><jupyter_text>That looks better! Now we can just add them up. The `math` module has a handy function to `sum` a list of numbers:<jupyter_code>(require math)
(sum (map string->number (regexp-match* #px"-?\\d+" data-str)))<jupyter_output><empty_output><jupyter_text>Which is the correct answer for Part 1.  Part 2For part 2, we are to exclude any `object` and its descendants in the json which has a VALUE `red`. Lists and scalars are to be left as-is. What's the sum now?For this we'll need to parse the json properly, so we start with requiring Racket's `json` module, and store the parsed json in a variable called `json-data`.<jupyter_code>(require json)
(define json-data (with-input-from-string data-str read-json))<jupyter_output><empty_output><jupyter_text>We need a predicate function to tell us if a json object (converted to a Racket hash) contains a particular value. The building blocks are `hash-values` which returns a list, and `member` which somewhat awkwardly returns either false, or the sub-list starting from the value.<jupyter_code>(define (hash-has-value? haystack needle)
  (if (not (member needle (hash-values haystack))) #f #t))<jupyter_output><empty_output><jupyter_text>We can test that. Testing is good.<jupyter_code>(hash-has-value? (hash "one" 1 "two" 2) 3)
(hash-has-value? (hash "one" 1 "two" 2) 2)<jupyter_output><empty_output><jupyter_text>JSON is defined recursively. Racket is good at processing recursive structures. We have four cases -- the json bit we're considering is a number, add that to our sum. If it's a list, then we need to recursively consider its components. If it's an object, we recursively consider its values, unless it contains a key `red`. In all other cases we skip.We get a lot of help from Racket's rich set of list comprehensions and sequence operators here, for example `for/sum`.<jupyter_code>(define (sum-without-reds json-value)
  (cond [(number? json-value) json-value]                                   ; number, just return it
        [(and (hash? json-value) (not (hash-has-value? json-value "red")))  ; object-without-red
          (for/sum ([v (in-hash-values json-value)]) 
            (sum-without-reds v))]                                          ; recursively sum its values
        [(list? json-value)                                                 ; array
         (for/sum ([v (in-list json-value)]) 
           (sum-without-reds v))]                                           ; recursively sum its members
        [else 0]))                                                          ; otherwise, skip; no change
(sum-without-reds json-data)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10130944176430048, 'fraction_numerical': 0.00781070526073972, 'mean_word_length': 3.6417910447761193, 'pattern_counts': {'":': 2, '<': 37, '<?xml version=': 0, '>': 41, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '526182', 'license': 'Apache-2.0', 'n_tokens_mistral': 1301, 'n_tokens_neox': 1231, 'n_words': 523, 'path': '2015-day12.ipynb', 'repo_name': 'xpqz/aoc-talk', 'size': '4353'}