Skip to content
catenav1.2.0
Theme
GitHub

Selection

Selection: finding extremes and top-k without sorting the world. TopNBy keeps a bounded heap of k — on a large scan that is the difference between kilobytes and the whole dataset in memory.

Run it: go run ./examples/03-selection

// Selection: finding extremes and top-k without sorting the world.
// TopNBy keeps a bounded heap of k — on a large scan that is the
// difference between kilobytes and the whole dataset in memory.
package main
import (
"fmt"
"github.com/NerdMeNot/catena"
)
type Player struct {
Name string
Score int
Level int
}
var players = []Player{
{"ada", 972, 12}, {"bob", 512, 9}, {"eve", 972, 11}, {"kim", 730, 12},
{"lou", 214, 3}, {"mia", 730, 8}, {"noa", 999, 14}, {"oli", 88, 2},
}
func main() {
s := catena.FromSlice(players)
// Top three by score. Output is sorted descending, and ties keep
// their encounter order — ada scored 972 before eve, so ada ranks
// ahead. That stability is guaranteed, not incidental.
for i, p := range s.TopNBy(3, func(p Player) int { return p.Score }) {
fmt.Printf("#%d %s (%d)\n", i+1, p.Name, p.Score)
}
// MaxBy returns the ELEMENT with the largest key; MaxOf the KEY
// itself. The -By/-Of distinction holds across the whole library.
best, _ := s.MaxBy(func(p Player) int { return p.Score })
high, _ := s.MaxOf(func(p Player) int { return p.Score })
fmt.Printf("best player: %s; high score: %d\n", best.Name, high)
// One pass for both ends of the range.
lo, hi, _ := s.MinMaxOf(func(p Player) int { return p.Level })
fmt.Printf("levels span %d%d\n", lo, hi)
// SortedBy is stable and calls its selector exactly once per element,
// so sorting by one key after another composes into a multi-level
// sort: level descending, then score descending within a level.
ranked := s.
SortedByDesc(func(p Player) int { return p.Score }).
SortedByDesc(func(p Player) int { return p.Level }).
Take(4).
Collect()
fmt.Println("leaderboard by level, then score:")
for _, p := range ranked {
fmt.Printf(" L%-2d %-4s %d\n", p.Level, p.Name, p.Score)
}
// Reduce for custom pairwise selection when no key exists: the
// player whose name sorts last, say.
last, _ := s.Reduce(func(a, b Player) Player {
if b.Name > a.Name {
return b
}
return a
})
fmt.Println("alphabetically last:", last.Name)
}

View on GitHub