[Odin] Collect, Sort, Format, Print[Odin] Collect, Sort, Format, Print
🏂
Winter Olympics
Week 6, 2026
package main
import "core:encoding/csv"
import "core:fmt"
import "core:slice"
import "core:strings"
input_bytes := #load("winter-olympics.csv")
Country_Stat :: struct {
name : string,
total_entries : int,
total_medals : int,
total_medal : [Medal] int,
total_medal_sex : [Sex] int,
}
Sex :: enum { male, female }
Medal :: enum { bronze, silver, gold }
main :: proc () {
countries := countries_stats(string(input_bytes))
slice.sort_by(countries, less=proc (a, b: Country_Stat) -> bool {
switch {
case a.total_medals != b.total_medals:
return a.total_medals > b.total_medals
case a.total_entries != b.total_entries:
return a.total_entries < b.total_entries
case:
return 0 > strings.compare(a.name, b.name)
}
})
for c in countries {
male_medal_percentage_str := c.total_medals > 0\
? fmt.tprintf(
" - meaning %.2f%% of medals have been won by men",
f32(c.total_medal_sex[.male]*100) / f32(c.total_medals),
) : ""
fmt.printfln(
"%s has won %i medals from %i entries - a win rate of %.2f%%. " +
"Their medal breakdown is %i golds, %i silvers and %i bronzes. " +
"%i men and %i women have won medals%s.",
c.name,
c.total_medals,
c.total_entries,
f32(c.total_medals*100) / f32(c.total_entries),
c.total_medal[.gold],
c.total_medal[.silver],
c.total_medal[.bronze],
c.total_medal_sex[.male],
c.total_medal_sex[.female],
male_medal_percentage_str,
)
}
}
countries_stats :: proc (csv_text: string, allocator := context.allocator) -> [] Country_Stat {
reader := csv.Reader { reuse_record_buffer=true, reuse_record=true }
csv.reader_init_with_string(&reader, csv_text, context.temp_allocator)
countries := make(map [string] Country_Stat, context.temp_allocator)
for rec, idx, err in csv.iterator_next(&reader) {
// fmt.println(err, idx, rec)
assert(err == nil)
if idx == 0 do continue // skip row with column names
rec_sex := rec[2]
rec_name := rec[4]
rec_medal := rec[12]
dash_idx := strings.last_index(rec_name, "-")
if dash_idx >= 0 {
rec_name = rec_name[:dash_idx]
}
if rec_name not_in countries {
c := Country_Stat { name=strings.clone(rec_name, allocator) }
countries[c.name] = c
}
c := &countries[rec_name]
c.total_entries += 1
if rec_medal != "" {
c.total_medals += 1
medal := parse_medal(rec_medal)
c.total_medal[medal] += 1
sex := parse_sex(rec_sex)
c.total_medal_sex[sex] += 1
}
}
result, _ := slice.map_values(countries, allocator=allocator)
return result
}
parse_sex :: proc (s: string) -> Sex {
switch s {
case "M": return .male
case "F": return .female
case : fmt.panicf("Unexpected sex: %s", s)
}
}
parse_medal :: proc (s: string) -> Medal {
switch s {
case "Bronze" : return .bronze
case "Silver" : return .silver
case "Gold" : return .gold
case : fmt.panicf("Unexpected medal: %s", s)
}
}