Dazbo's Advent of Code solutions, written in Python
We’re told that Santa’s needs some help balancing the books. His accounting data is stored in JSON format, made up of:
Type | E.g. |
---|---|
Arrays | [1,2,3] |
Objects | {“a”:1, “b”:2} |
Numbers | 1 |
Strings | “whatever” |
Take a look at the image above for an indication of what our sample data looks like.
What is the sum of all numbers in the input document?
Once again, I’ve opted to do a bit of recursion.
First, note that the data types in our JSON input directly correlate to Python data types:
Type | E.g. | Python Data Type |
---|---|---|
Arrays | [1,2,3] | list |
Objects | {“a”:1, “b”:2} | dict |
Numbers | 1 | int |
Strings | “whatever” | str |
My plan of action is as follows:
First, let’s read in the JSON:
import os
import time
import json
SCRIPT_DIR = os.path.dirname(__file__)
INPUT_FILE = "input/input.json"
def main():
input_file = os.path.join(SCRIPT_DIR, INPUT_FILE)
with open(input_file, mode="rt") as f:
j = json.load(f)
result = process_json(j)
print(f"Total of all numbers: {result}")
Here we’re using json.load(f)
to read in the JSON input file, and return it as a Python dictionary. It always returns a dict
because the JSON data always has {
and }
at the outermost level.
We pass our JSON dict to our process_json(data)
method:
def process_json(json_input):
""" Recursively processes json input.
Identifies all int values stored in a json object, and adds them up.
Args:
json_input (str): any valid json input
Returns:
int: Sum of all ints in this object
"""
num_total = 0
if isinstance(json_input, dict):
for key in json_input:
num_total += process_json(json_input[key], ignore)
elif isinstance(json_input, list):
for element in json_input:
num_total += process_json(element, ignore)
elif isinstance(json_input, int):
num_total += json_input # base case that doesn't recurse
# Might also be str type, but we don't care about those, so we can ignore this case.
return num_total
The only new thing to mention here is the use of isinstance(some_object, some_python_object_type)
, which returns True
if the object passed in matches the specified type.
That was easy enough!
We’re told we must ignore any object - including all of its children - that has any property with the value red
.
We’re told to do this for objects ({ ... }
) and not for arrays ([ ... ]
).
We can do this with very little additional coding. Since we’re told to only ignore objects, this means we only need to apply this red
rule for dict
values in our data.
My solution is to add an optional parameter to our function, allowing us to pass in a value that we should ignore in any dictionary types:
def process_json(json_input, ignore=None):
""" Recursively processes json input.
Identifies all int values stored in a json object, and adds them up.
Args:
json_input (str): any valid json input
ignore (str, optional): Ignore any collection or value that has an element with this value.
Defaults to None.
Returns:
int: Sum of all ints in this object
"""
num_total = 0
if isinstance(json_input, dict):
if ignore and ignore in json_input.values():
return 0
for key in json_input:
num_total += process_json(json_input[key], ignore)
elif isinstance(json_input, list):
for element in json_input:
num_total += process_json(element, ignore)
elif isinstance(json_input, int):
num_total += json_input # base case that doesn't recurse
# Might also be str type, but we don't care about those, so we can ignore this case.
return num_total
And then we just need to call this same function, and pass in the string value we want to ignore:
# Part 2
result = process_json(j, "red")
print(f"Total of all numbers: {result}")
And that’s it!
Here’s the output:
Total of all numbers: 111754
Total of all numbers: 65402
Execution time: 0.0035 seconds
That’s pretty darn fast!