package main // TODO(kr): tests import ( "bufio" "fmt" "log" "math/rand" "os" "strings" "time" ) type agg interface { merge(string) String() string } var ( key = 0 funcmap = make(map[int]func(init, arg string) agg) argmap = make(map[int]string) symtab = map[string]func(init, arg string) agg{ "first": first, "last": last, "prefix": prefix, "sample": sample, "join": join, "smin": smin, "smax": smax, "min": min, "max": max, "sum": sum, "mean": mean, "count": count, "const": constf, "drop": nil, } ) func main() { log.SetPrefix("agg: ") log.SetFlags(0) rand.Seed(time.Now().UnixNano()) for i, sym := range os.Args[1:] { if p := strings.IndexByte(sym, ':'); p >= 0 { sym, argmap[i] = sym[:p], sym[p+1:] } if sym == "key" { key, sym = i, "first" } f, ok := symtab[sym] if !ok { log.Fatalf("bad function: %q", sym) } funcmap[i] = f } sc := bufio.NewScanner(os.Stdin) var g *group for sc.Scan() { ss := strings.Fields(sc.Text()) if !matches(g, ss) { emit(g) g = &group{key: ss[key]} } mergeLine(g, ss) } emit(g) } type group struct { key string agg []agg } func matches(g *group, ss []string) bool { return g != nil && g.key == ss[key] } func emit(g *group) { if g == nil { return } rest := false for i, a := range g.agg { if f, ok := funcmap[i]; ok && f == nil { continue } if rest { fmt.Print("\t") } rest = true fmt.Print(a) } fmt.Println() } func mergeLine(g *group, ss []string) { for i, s := range ss { if i >= len(g.agg) { f := funcmap[i] if f == nil { f = first } g.agg = append(g.agg, f(s, argmap[i])) } else { g.agg[i].merge(s) } } }