Top rank per group

var data = <<'EOF'.lines.map{ <name id salary dept> ~Z .split(',') -> flat.to_h }
Tyler Bennett,E10297,32000,D101
John Rappl,E21437,47000,D050
George Woltman,E00127,53500,D101
Adam Smith,E63535,18000,D202
Claire Buckman,E39876,27800,D202
David McClellan,E04242,41500,D101
Rich Holcomb,E01234,49500,D202
Nathan Adams,E41298,21900,D050
Richard Potter,E43128,15900,D101
David Motsinger,E27002,19250,D202
Tim Sampair,E03033,27000,D101
Kim Arlich,E10001,57000,D190
Timothy Grove,E16398,29900,D190
EOF

var n = (ARGV ? Num(ARGV[0]) : "usage: #{__MAIN__} [n]\n".die)

for d in (data.map {|h| h{:dept} }.uniq.sort) {
    var es = data.grep { _{:dept} == d }.sort_by { -Num(_{:salary}) }
    say d
    n.times {
        es || break
        printf("%-15s | %-6s | %5d\n", es.shift(){<name id salary>...})
    }
    print "\n"
}

Output:

$ sidef top_rank.sf 3
D050
John Rappl      | E21437 | 47000
Nathan Adams    | E41298 | 21900

D101
George Woltman  | E00127 | 53500
David McClellan | E04242 | 41500
Tyler Bennett   | E10297 | 32000

D190
Kim Arlich      | E10001 | 57000
Timothy Grove   | E16398 | 29900

D202
Rich Holcomb    | E01234 | 49500
Claire Buckman  | E39876 | 27800
David Motsinger | E27002 | 19250