[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) } }