Sort and summarize values 🔥
Once you’ve collected your puzzle data, you usually need to make sense of it. Which values are largest or smallest? What’s the average? Which readings cross a threshold? Sorting, reducing, filtering, and matching values back to their source can turn a pile of observations into answers.
In this chapter, you’ll return to the station readings and look at them from several angles. You’ll sort them to find the highest values, calculate an average, filter for extremes, and pair readings with their original labels. Then you’ll skip the sort entirely and find the minimum and maximum in a single pass, because sometimes the best way to summarize data is to avoid doing work you don’t need.
The station readings
Create sort_and_summarize_values.mojo. Read
station_reports.txt and pull out each
reading’s temperature, alongside a label so you can trace a value back to
the entry it came from:
var log = Path("src/downloads/station_reports.txt")
var lines = log.read_text().splitlines()
var labels = List[String]()
var readings: List[Float64] = []
for line in lines:
# ['Station 3', 'Day 0', '06:00', '-22.3C', 'Clear']
var fields = line.split(", ")
labels.append(String(t"{fields[0]}, {fields[1]}, {fields[2]}"))
readings.append(Float64(fields[3].removesuffix("C")))
var count = len(readings)
print(t"{count} readings") # 15 readings
Sort to rank
Sorting orders the list, so you can pick the coldest and warmest readings from the two ends.
Import sort at the top of your file:
from std.builtin.sort import sort
Sort a copy so the original order stays intact:
var ordered = readings.copy()
sort(ordered)
print(t"sorted: {ordered}")
# [-41.7, -32.0, -31.5, -30.1, -29.0, -28.4, -24.0, -23.1, -22.3, -22.3,
# -21.7, -21.0, -20.5, -18.5, -18.2]
print(t"coldest: {ordered[0]}, warmest: {ordered[count - 1]}")
Takeaways
sort()is a free function, not a method. It sorts in place and ascending, so it returns nothing and rewrites the list you pass.- Sort a
.copy()when you need the original ordering. Herereadingsstays aligned withlabels.
Top of the list
Once a list is sorted, the largest values are at the end. Slice off the last three to retrieve them in ascending order:
print(t"top 3 warmest: {ordered[(count - 3):]}") # [-20.5, -18.5, -18.2]
Takeaways
- This provides the “top three” that many puzzles ask for: sort once, then take from the end.
Reduce your data
You’ve been asked to find the average temperature across every reading.
Iterate a sum and divide by count.
var total: Float64 = 0.0
for r in readings:
total += r
print(t"average: {round(total / Float64(count), 1)}") # -25.6
Takeaways
round(value, digits)tidies a float for display. Floating-point totals carry representation noise.round()helps you control that.Listhas no built-in reductions likesum(),min(), ormax(). Accumulate across a loop instead. Themin()andmax()instd.mathcompare two values, not a whole list.
Filter it down
Use a comprehension filtered for the most extreme readings to build a new list:
var extreme = [r for r in readings if r < -30.0]
print(t"extreme readings: {extreme}") # [-30.1, -31.5, -32.0, -41.7]
Takeaways
[expr for x in it if cond]keeps only the elements wherecondis true.- The filter runs as the list builds, so you never construct the rejects.
Pair the readings with their labels
The labels list has important information you need to tell which
station and day a reading belongs to.
zip() combines two lists, allowing you to walk through paired values in step.
Use this to align readings with their labels:
for label, r in zip(labels, readings):
print(t"{label}: {r}")
Takeaways
zip()yields a tuple per step, unpacked here intolabelandr.
Match the readings to the extremes
Sorting orders every reading when you only need two of them. A single pass finds both extremes without that work:
var max_value = Float64.MIN # -inf, so any reading beats it
var min_value = Float64.MAX # +inf, so any reading undercuts it
for r in readings: # one pass finds both extremes
if r > max_value:
max_value = r
if r < min_value:
min_value = r
# max_value: -18.2 (warmest), min_value: -41.7 (coldest)
Knowing the values helps you find their original indices in readings:
try:
var min_index = readings.index(min_value)
print(t"Coldest reading: {labels[min_index]} at {min_value}°C")
var max_index = readings.index(max_value)
print(t"Warmest reading: {labels[max_index]} at {max_value}°C")
except e:
print(e)
Wrapping your calls to index() in a try/except statement ensures
that any errors will be caught and reported.
If you’d like to see that error in action, tweak either value. For example
.index(min_value + 1.0).
Final code
Your complete sort_and_summarize_values.mojo:
from std.pathlib import Path
from std.builtin.sort import sort
def main() raises:
var log = Path("src/downloads/station_reports.txt")
var lines = log.read_text().splitlines()
var labels = List[String]()
var readings: List[Float64] = []
for line in lines:
# ['Station 3', 'Day 0', '06:00', '-22.3C', 'Clear']
var fields = line.split(", ")
labels.append(String(t"{fields[0]}, {fields[1]}, {fields[2]}"))
readings.append(Float64(fields[3].removesuffix("C")))
var count = len(readings)
print(t"{count} readings") # 15 readings
var ordered = readings.copy()
sort(ordered)
print(t"sorted: {ordered}")
# [-41.7, -32.0, -31.5, -30.1, -29.0, -28.4, -24.0, -23.1, -22.3, -22.3,
# -21.7, -21.0, -20.5, -18.5, -18.2]
print(t"coldest: {ordered[0]}, warmest: {ordered[count - 1]}")
print(t"top 3 warmest: {ordered[(count - 3):]}") # [-20.5, -18.5, -18.2]
var total: Float64 = 0.0
for r in readings:
total += r
print(t"average: {round(total / Float64(count), 1)}") # -25.6
var extreme = [r for r in readings if r < -30.0]
print(t"extreme readings: {extreme}") # [-30.1, -31.5, -32.0, -41.7]
for label, r in zip(labels, readings):
print(t"{label}: {r}")
var max_value = Float64.MIN # -inf, so any reading beats it
var min_value = Float64.MAX # +inf, so any reading undercuts it
for r in readings: # one pass finds both extremes
if r > max_value:
max_value = r
if r < min_value:
min_value = r
# max_value: -18.2 (warmest), min_value: -41.7 (coldest)
try:
var min_index = readings.index(min_value)
print(t"Coldest reading: {labels[min_index]} at {min_value}°C")
var max_index = readings.index(max_value)
print(t"Warmest reading: {labels[max_index]} at {max_value}°C")
except e:
print(e)
Topics covered
Sorting, filtered comprehensions, zip(), and locating values with
index().