func SortBy(g Grouping, cols ...string) Grouping {
	sorters := make([]sort.Interface, len(cols))
	return MapTables(g, func(_ GroupID, t *Table) *Table {
		sorters = sorters[:0]
		for _, col := range cols {
			if _, ok := t.Const(col); ok {
				continue
			}
			seq := t.MustColumn(col)
			sorter := slice.Sorter(seq)
			if sort.IsSorted(sorter) {
				continue
			}
			sorters = append(sorters, sorter)
		}
		if len(sorters) == 0 {
			return t
		}
		perm := make([]int, t.Len())
		for i := range perm {
			perm[i] = i
		}
		sort.Stable(&permSort{perm, sorters})
		var nt Builder
		for _, name := range t.Columns() {
			if cv, ok := t.Const(name); ok {
				nt.AddConst(name, cv)
				continue
			}
			seq := t.Column(name)
			seq = slice.Select(seq, perm)
			nt.Add(name, seq)
		}
		return nt.Done()
	})
}
SortBy sorts each group of g by the named columns. If a column's
type implements sort.Interface, rows will be sorted according to
that order. Otherwise, the values in the column must be naturally
ordered (their types must be orderable by the Go specification). If
neither is true, SortBy panics with a *generic.TypeError. If more
than one column is given, SortBy sorts by the tuple of the columns;
that is, if two values in the first column are equal, they are
sorted by the second column, and so on.
package table
import (
	"sort"
	"github.com/aclements/go-gg/generic/slice"
)