func (ix *IdxView) SortStableCol(colIdx int, ascending bool) {
	cl := ix.Table.Cols[colIdx]
	if cl.DataType() == etensor.STRING {
		ix.SortStable(func(et *Table, i, j int) bool {
			if ascending {
				return cl.StringVal1D(i) < cl.StringVal1D(j)
			} else {
				return cl.StringVal1D(i) > cl.StringVal1D(j)
			}
		})
	} else {
		ix.SortStable(func(et *Table, i, j int) bool {
			if ascending {
				return cl.FloatVal1D(i) < cl.FloatVal1D(j)
			} else {
				return cl.FloatVal1D(i) > cl.FloatVal1D(j)
			}
		})
	}
}
SortStableCol sorts the indexes into our Table according to values in
given column index, using either ascending or descending order.
Only valid for 1-dimensional columns.
package etable
import (
	"fmt"
	"log"
	"math"
	"math/rand"
	"sort"
	"strings"
	"github.com/emer/etable/etensor"
	"github.com/goki/ki/ki"
	"github.com/goki/ki/kit"
	"github.com/goki/ki/sliceclone"
)
type LessFunc func(et *Table, i, j int) bool
type FilterFunc func(et *Table, row int) bool
type IdxView struct {
	Table    *Table   `desc:"Table that we are an indexed view onto"`
	Idxs     []int    `desc:"current indexes into Table"`
	lessFunc LessFunc `copy:"-" view:"-" xml:"-" json:"-" desc:"current Less function used in sorting"`
}
var KiT_IdxView = kit.Types.AddType(&IdxView{}, IdxViewProps)
func NewIdxView(et *Table) *IdxView {
	ix := &IdxView{}
	ix.SetTable(et)
	return ix
}
func (ix *IdxView) SetTable(et *Table) {
	ix.Table = et
	ix.Sequential()
}
func (ix *IdxView) DeleteInvalid() {
	if ix.Table == nil || ix.Table.Rows <= 0 {
		ix.Idxs = nil
		return
	}
	ni := ix.Len()
	for i := ni - 1; i >= 0; i-- {
		if ix.Idxs[i] >= ix.Table.Rows {
			ix.Idxs = append(ix.Idxs[:i], ix.Idxs[i+1:]...)
		}
	}
}
func (ix *IdxView) Sequential() {
	if ix.Table == nil || ix.Table.Rows <= 0 {
		ix.Idxs = nil
		return
	}
	ix.Idxs = make([]int, ix.Table.Rows)
	for i := range ix.Idxs {
		ix.Idxs[i] = i
	}
}
func (ix *IdxView) Permuted() {
	if ix.Table == nil || ix.Table.Rows <= 0 {
		ix.Idxs = nil
		return
	}
	if len(ix.Idxs) == 0 {
		ix.Idxs = rand.Perm(ix.Table.Rows)
	} else {
		rand.Shuffle(len(ix.Idxs), func(i, j int) {
			ix.Idxs[i], ix.Idxs[j] = ix.Idxs[j], ix.Idxs[i]
		})
	}
}
func (ix *IdxView) AddIndex(idx int) {
	ix.Idxs = append(ix.Idxs, idx)
}
func (ix *IdxView) Sort(lessFunc func(et *Table, i, j int) bool) {
	ix.lessFunc = lessFunc
	sort.Sort(ix)
}
func (ix *IdxView) SortIdxs() {
	sort.Ints(ix.Idxs)
}
const (
	Ascending = true
	Descending = false
)
func (ix *IdxView) SortColName(colNm string, ascending bool) error {
	ci, err := ix.Table.ColIdxTry(colNm)
	if err != nil {
		log.Println(err)
		return err
	}
	ix.SortCol(ci, ascending)
	return nil
}
func (ix *IdxView) SortCol(colIdx int, ascending bool) {
	cl := ix.Table.Cols[colIdx]
	if cl.DataType() == etensor.STRING {
		ix.Sort(func(et *Table, i, j int) bool {
			if ascending {
				return cl.StringVal1D(i) < cl.StringVal1D(j)
			} else {
				return cl.StringVal1D(i) > cl.StringVal1D(j)
			}
		})
	} else {
		ix.Sort(func(et *Table, i, j int) bool {
			if ascending {
				return cl.FloatVal1D(i) < cl.FloatVal1D(j)
			} else {
				return cl.FloatVal1D(i) > cl.FloatVal1D(j)
			}
		})
	}
}
func (ix *IdxView) SortColNames(colNms []string, ascending bool) error {
	nc := len(colNms)
	if nc == 0 {
		return fmt.Errorf("etable.IdxView.SortColNames: no column names provided")
	}
	cis := make([]int, nc)
	for i, cn := range colNms {
		ci, err := ix.Table.ColIdxTry(cn)
		if err != nil {
			log.Println(err)
			return err
		}
		cis[i] = ci
	}
	ix.SortCols(cis, ascending)
	return nil
}
func (ix *IdxView) SortCols(colIdxs []int, ascending bool) {
	ix.Sort(func(et *Table, i, j int) bool {
		for _, ci := range colIdxs {
			cl := ix.Table.Cols[ci]
			if cl.DataType() == etensor.STRING {
				if ascending {
					if cl.StringVal1D(i) < cl.StringVal1D(j) {
						return true
					} else if cl.StringVal1D(i) > cl.StringVal1D(j) {
						return false
					}  
				} else {
					if cl.StringVal1D(i) > cl.StringVal1D(j) {
						return true
					} else if cl.StringVal1D(i) < cl.StringVal1D(j) {
						return false
					}  
				}
			} else {
				if ascending {
					if cl.FloatVal1D(i) < cl.FloatVal1D(j) {
						return true
					} else if cl.FloatVal1D(i) < cl.FloatVal1D(j) {
						return false
					}  
				} else {
					if cl.FloatVal1D(i) > cl.FloatVal1D(j) {
						return true
					} else if cl.FloatVal1D(i) < cl.FloatVal1D(j) {
						return false
					}  
				}
			}
		}
		return false
	})
}
func (ix *IdxView) SortStable(lessFunc func(et *Table, i, j int) bool) {
	ix.lessFunc = lessFunc
	sort.Stable(ix)
}
func (ix *IdxView) SortStableColName(colNm string, ascending bool) error {
	ci, err := ix.Table.ColIdxTry(colNm)
	if err != nil {
		log.Println(err)
		return err
	}
	ix.SortStableCol(ci, ascending)
	return nil
}