github.com/goki/ki@v1.1.17/kit/slices.go (about)

     1  // Copyright (c) 2018, The GoKi Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package kit
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/goki/ki/floats"
    16  	"github.com/goki/ki/ints"
    17  )
    18  
    19  // This file contains helpful functions for dealing with slices, in the reflect
    20  // system
    21  
    22  // MakeSlice makes a map that is actually addressable, getting around the hidden
    23  // interface{} that reflect.MakeSlice makes, by calling UnhideIfaceValue (from ptrs.go)
    24  func MakeSlice(typ reflect.Type, len, cap int) reflect.Value {
    25  	return UnhideIfaceValue(reflect.MakeSlice(typ, len, cap))
    26  }
    27  
    28  // SliceElType returns the type of the elements for the given slice (which can be
    29  // a pointer to a slice or a direct slice) -- just Elem() of slice type, but using
    30  // this function makes it more explicit what is going on.  And it uses
    31  // OnePtrUnderlyingValue to get past any interface wrapping.
    32  func SliceElType(sl any) reflect.Type {
    33  	return NonPtrValue(OnePtrUnderlyingValue(reflect.ValueOf(sl))).Type().Elem()
    34  }
    35  
    36  // SliceNewAt inserts a new blank element at given index in the slice -- -1
    37  // means the end
    38  func SliceNewAt(sl any, idx int) {
    39  	sltyp := SliceElType(sl)
    40  	slptr := sltyp.Kind() == reflect.Ptr
    41  
    42  	svl := reflect.ValueOf(sl)
    43  	svnp := NonPtrValue(svl)
    44  
    45  	nval := reflect.New(NonPtrType(sltyp)) // make the concrete el
    46  	if !slptr {
    47  		nval = nval.Elem() // use concrete value
    48  	}
    49  	sz := svnp.Len()
    50  	svnp = reflect.Append(svnp, nval)
    51  	if idx >= 0 && idx < sz {
    52  		reflect.Copy(svnp.Slice(idx+1, sz+1), svnp.Slice(idx, sz))
    53  		svnp.Index(idx).Set(nval)
    54  	}
    55  	svl.Elem().Set(svnp)
    56  }
    57  
    58  // SliceDeleteAt deletes element at given index from slice
    59  func SliceDeleteAt(sl any, idx int) {
    60  	svl := reflect.ValueOf(sl)
    61  	svnp := NonPtrValue(svl)
    62  	svtyp := svnp.Type()
    63  	nval := reflect.New(svtyp.Elem())
    64  	sz := svnp.Len()
    65  	reflect.Copy(svnp.Slice(idx, sz-1), svnp.Slice(idx+1, sz))
    66  	svnp.Index(sz - 1).Set(nval.Elem())
    67  	svl.Elem().Set(svnp.Slice(0, sz-1))
    68  }
    69  
    70  // SliceSort sorts a slice of basic values (see StructSliceSort for sorting a
    71  // slice-of-struct using a specific field), trying floats.Floater Float(),
    72  // ints.Inter Int(), interfaces first, and then falling back on reflect.Kind
    73  // float, int, string conversions (first fmt.Stringer String()) and supporting
    74  // time.Time directly as well.
    75  func SliceSort(sl any, ascending bool) error {
    76  	sv := reflect.ValueOf(sl)
    77  	svnp := NonPtrValue(sv)
    78  	if svnp.Len() == 0 {
    79  		return nil
    80  	}
    81  	eltyp := SliceElType(sl)
    82  	elnptyp := NonPtrType(eltyp)
    83  	vk := elnptyp.Kind()
    84  	elval := OnePtrValue(svnp.Index(0))
    85  	elif := elval.Interface()
    86  
    87  	switch elif.(type) {
    88  	case floats.Floater:
    89  		sort.Slice(svnp.Interface(), func(i, j int) bool {
    90  			iv := NonPtrValue(svnp.Index(i)).Interface().(floats.Floater).Float()
    91  			jv := NonPtrValue(svnp.Index(j)).Interface().(floats.Floater).Float()
    92  			if ascending {
    93  				return iv < jv
    94  			}
    95  			return iv > jv
    96  		})
    97  		return nil
    98  	case ints.Inter:
    99  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   100  			iv := NonPtrValue(svnp.Index(i)).Interface().(ints.Inter).Int()
   101  			jv := NonPtrValue(svnp.Index(j)).Interface().(ints.Inter).Int()
   102  			if ascending {
   103  				return iv < jv
   104  			}
   105  			return iv > jv
   106  		})
   107  		return nil
   108  	}
   109  
   110  	// try all the numeric types first!
   111  
   112  	switch {
   113  	case vk >= reflect.Int && vk <= reflect.Int64:
   114  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   115  			iv := NonPtrValue(svnp.Index(i)).Int()
   116  			jv := NonPtrValue(svnp.Index(j)).Int()
   117  			if ascending {
   118  				return iv < jv
   119  			}
   120  			return iv > jv
   121  		})
   122  		return nil
   123  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   124  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   125  			iv := NonPtrValue(svnp.Index(i)).Uint()
   126  			jv := NonPtrValue(svnp.Index(j)).Uint()
   127  			if ascending {
   128  				return iv < jv
   129  			}
   130  			return iv > jv
   131  		})
   132  		return nil
   133  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   134  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   135  			iv := NonPtrValue(svnp.Index(i)).Float()
   136  			jv := NonPtrValue(svnp.Index(j)).Float()
   137  			if ascending {
   138  				return iv < jv
   139  			}
   140  			return iv > jv
   141  		})
   142  		return nil
   143  	case vk == reflect.Struct && ShortTypeName(elnptyp) == "time.Time":
   144  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   145  			iv := NonPtrValue(svnp.Index(i)).Interface().(time.Time)
   146  			jv := NonPtrValue(svnp.Index(j)).Interface().(time.Time)
   147  			if ascending {
   148  				return iv.Before(jv)
   149  			}
   150  			return jv.Before(iv)
   151  		})
   152  	}
   153  
   154  	// this stringer case will likely pick up most of the rest
   155  	switch elif.(type) {
   156  	case fmt.Stringer:
   157  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   158  			iv := NonPtrValue(svnp.Index(i)).Interface().(fmt.Stringer).String()
   159  			jv := NonPtrValue(svnp.Index(j)).Interface().(fmt.Stringer).String()
   160  			if ascending {
   161  				return iv < jv
   162  			}
   163  			return iv > jv
   164  		})
   165  		return nil
   166  	}
   167  
   168  	// last resort!
   169  	switch {
   170  	case vk == reflect.String:
   171  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   172  			iv := NonPtrValue(svnp.Index(i)).String()
   173  			jv := NonPtrValue(svnp.Index(j)).String()
   174  			if ascending {
   175  				return strings.ToLower(iv) < strings.ToLower(jv)
   176  			}
   177  			return strings.ToLower(iv) > strings.ToLower(jv)
   178  		})
   179  		return nil
   180  	}
   181  
   182  	err := fmt.Errorf("SortSlice: unable to sort elements of type: %v", eltyp.String())
   183  	log.Println(err)
   184  	return err
   185  }
   186  
   187  // StructSliceSort sorts a slice of a struct according to the given field
   188  // indexes and sort direction, trying floats.Floater Float(), ints.Inter Int(),
   189  // interfaces first, and then falling back on reflect.Kind float, int, string
   190  // conversions (first fmt.Stringer String()) and supporting time.Time directly
   191  // as well.  There is no direct method for checking the field indexes so those
   192  // are assumed to be accurate -- will panic if not!
   193  func StructSliceSort(struSlice any, fldIdx []int, ascending bool) error {
   194  	sv := reflect.ValueOf(struSlice)
   195  	svnp := NonPtrValue(sv)
   196  	if svnp.Len() == 0 {
   197  		return nil
   198  	}
   199  	struTyp := SliceElType(struSlice)
   200  	struNpTyp := NonPtrType(struTyp)
   201  	fld := struNpTyp.FieldByIndex(fldIdx) // not easy to check.
   202  	vk := fld.Type.Kind()
   203  	struVal := OnePtrValue(svnp.Index(0))
   204  	fldVal := struVal.Elem().FieldByIndex(fldIdx)
   205  	fldIf := fldVal.Interface()
   206  
   207  	switch fldIf.(type) {
   208  	case floats.Floater:
   209  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   210  			ival := OnePtrValue(svnp.Index(i))
   211  			iv := ival.Elem().FieldByIndex(fldIdx).Interface().(floats.Floater).Float()
   212  			jval := OnePtrValue(svnp.Index(j))
   213  			jv := jval.Elem().FieldByIndex(fldIdx).Interface().(floats.Floater).Float()
   214  			if ascending {
   215  				return iv < jv
   216  			}
   217  			return iv > jv
   218  		})
   219  		return nil
   220  	case ints.Inter:
   221  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   222  			ival := OnePtrValue(svnp.Index(i))
   223  			iv := ival.Elem().FieldByIndex(fldIdx).Interface().(ints.Inter).Int()
   224  			jval := OnePtrValue(svnp.Index(j))
   225  			jv := jval.Elem().FieldByIndex(fldIdx).Interface().(ints.Inter).Int()
   226  			if ascending {
   227  				return iv < jv
   228  			}
   229  			return iv > jv
   230  		})
   231  		return nil
   232  	}
   233  
   234  	// try all the numeric types first!
   235  
   236  	switch {
   237  	case vk >= reflect.Int && vk <= reflect.Int64:
   238  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   239  			ival := OnePtrValue(svnp.Index(i))
   240  			iv := ival.Elem().FieldByIndex(fldIdx).Int()
   241  			jval := OnePtrValue(svnp.Index(j))
   242  			jv := jval.Elem().FieldByIndex(fldIdx).Int()
   243  			if ascending {
   244  				return iv < jv
   245  			}
   246  			return iv > jv
   247  		})
   248  		return nil
   249  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   250  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   251  			ival := OnePtrValue(svnp.Index(i))
   252  			iv := ival.Elem().FieldByIndex(fldIdx).Uint()
   253  			jval := OnePtrValue(svnp.Index(j))
   254  			jv := jval.Elem().FieldByIndex(fldIdx).Uint()
   255  			if ascending {
   256  				return iv < jv
   257  			}
   258  			return iv > jv
   259  		})
   260  		return nil
   261  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   262  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   263  			ival := OnePtrValue(svnp.Index(i))
   264  			iv := ival.Elem().FieldByIndex(fldIdx).Float()
   265  			jval := OnePtrValue(svnp.Index(j))
   266  			jv := jval.Elem().FieldByIndex(fldIdx).Float()
   267  			if ascending {
   268  				return iv < jv
   269  			}
   270  			return iv > jv
   271  		})
   272  		return nil
   273  	case vk == reflect.Struct && ShortTypeName(fld.Type) == "time.Time":
   274  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   275  			ival := OnePtrValue(svnp.Index(i))
   276  			iv := ival.Elem().FieldByIndex(fldIdx).Interface().(time.Time)
   277  			jval := OnePtrValue(svnp.Index(j))
   278  			jv := jval.Elem().FieldByIndex(fldIdx).Interface().(time.Time)
   279  			if ascending {
   280  				return iv.Before(jv)
   281  			}
   282  			return jv.Before(iv)
   283  		})
   284  	}
   285  
   286  	// this stringer case will likely pick up most of the rest
   287  	switch fldIf.(type) {
   288  	case fmt.Stringer:
   289  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   290  			ival := OnePtrValue(svnp.Index(i))
   291  			iv := ival.Elem().FieldByIndex(fldIdx).Interface().(fmt.Stringer).String()
   292  			jval := OnePtrValue(svnp.Index(j))
   293  			jv := jval.Elem().FieldByIndex(fldIdx).Interface().(fmt.Stringer).String()
   294  			if ascending {
   295  				return iv < jv
   296  			}
   297  			return iv > jv
   298  		})
   299  		return nil
   300  	}
   301  
   302  	// last resort!
   303  	switch {
   304  	case vk == reflect.String:
   305  		sort.Slice(svnp.Interface(), func(i, j int) bool {
   306  			ival := OnePtrValue(svnp.Index(i))
   307  			iv := ival.Elem().FieldByIndex(fldIdx).String()
   308  			jval := OnePtrValue(svnp.Index(j))
   309  			jv := jval.Elem().FieldByIndex(fldIdx).String()
   310  			if ascending {
   311  				return strings.ToLower(iv) < strings.ToLower(jv)
   312  			}
   313  			return strings.ToLower(iv) > strings.ToLower(jv)
   314  		})
   315  		return nil
   316  	}
   317  
   318  	err := fmt.Errorf("SortStructSlice: unable to sort on field of type: %v\n", fld.Type.String())
   319  	log.Println(err)
   320  	return err
   321  }
   322  
   323  // ValueSliceSort sorts a slice of reflect.Values using basic types where possible
   324  func ValueSliceSort(sl []reflect.Value, ascending bool) error {
   325  	if len(sl) == 0 {
   326  		return nil
   327  	}
   328  	felval := sl[0] // reflect.Value
   329  	eltyp := felval.Type()
   330  	elnptyp := NonPtrType(eltyp)
   331  	vk := elnptyp.Kind()
   332  	elval := OnePtrValue(felval)
   333  	elif := elval.Interface()
   334  
   335  	switch elif.(type) {
   336  	case floats.Floater:
   337  		sort.Slice(sl, func(i, j int) bool {
   338  			iv := NonPtrValue(sl[i]).Interface().(floats.Floater).Float()
   339  			jv := NonPtrValue(sl[j]).Interface().(floats.Floater).Float()
   340  			if ascending {
   341  				return iv < jv
   342  			}
   343  			return iv > jv
   344  		})
   345  		return nil
   346  	case ints.Inter:
   347  		sort.Slice(sl, func(i, j int) bool {
   348  			iv := NonPtrValue(sl[i]).Interface().(ints.Inter).Int()
   349  			jv := NonPtrValue(sl[j]).Interface().(ints.Inter).Int()
   350  			if ascending {
   351  				return iv < jv
   352  			}
   353  			return iv > jv
   354  		})
   355  		return nil
   356  	}
   357  
   358  	// try all the numeric types first!
   359  
   360  	switch {
   361  	case vk >= reflect.Int && vk <= reflect.Int64:
   362  		sort.Slice(sl, func(i, j int) bool {
   363  			iv := NonPtrValue(sl[i]).Int()
   364  			jv := NonPtrValue(sl[j]).Int()
   365  			if ascending {
   366  				return iv < jv
   367  			}
   368  			return iv > jv
   369  		})
   370  		return nil
   371  	case vk >= reflect.Uint && vk <= reflect.Uint64:
   372  		sort.Slice(sl, func(i, j int) bool {
   373  			iv := NonPtrValue(sl[i]).Uint()
   374  			jv := NonPtrValue(sl[j]).Uint()
   375  			if ascending {
   376  				return iv < jv
   377  			}
   378  			return iv > jv
   379  		})
   380  		return nil
   381  	case vk >= reflect.Float32 && vk <= reflect.Float64:
   382  		sort.Slice(sl, func(i, j int) bool {
   383  			iv := NonPtrValue(sl[i]).Float()
   384  			jv := NonPtrValue(sl[j]).Float()
   385  			if ascending {
   386  				return iv < jv
   387  			}
   388  			return iv > jv
   389  		})
   390  		return nil
   391  	case vk == reflect.Struct && ShortTypeName(elnptyp) == "time.Time":
   392  		sort.Slice(sl, func(i, j int) bool {
   393  			iv := NonPtrValue(sl[i]).Interface().(time.Time)
   394  			jv := NonPtrValue(sl[j]).Interface().(time.Time)
   395  			if ascending {
   396  				return iv.Before(jv)
   397  			}
   398  			return jv.Before(iv)
   399  		})
   400  	}
   401  
   402  	// this stringer case will likely pick up most of the rest
   403  	switch elif.(type) {
   404  	case fmt.Stringer:
   405  		sort.Slice(sl, func(i, j int) bool {
   406  			iv := NonPtrValue(sl[i]).Interface().(fmt.Stringer).String()
   407  			jv := NonPtrValue(sl[j]).Interface().(fmt.Stringer).String()
   408  			if ascending {
   409  				return iv < jv
   410  			}
   411  			return iv > jv
   412  		})
   413  		return nil
   414  	}
   415  
   416  	// last resort!
   417  	switch {
   418  	case vk == reflect.String:
   419  		sort.Slice(sl, func(i, j int) bool {
   420  			iv := NonPtrValue(sl[i]).String()
   421  			jv := NonPtrValue(sl[j]).String()
   422  			if ascending {
   423  				return strings.ToLower(iv) < strings.ToLower(jv)
   424  			}
   425  			return strings.ToLower(iv) > strings.ToLower(jv)
   426  		})
   427  		return nil
   428  	}
   429  
   430  	err := fmt.Errorf("ValueSliceSort: unable to sort elements of type: %v", eltyp.String())
   431  	log.Println(err)
   432  	return err
   433  }
   434  
   435  // CopySliceRobust robustly copies slices using SetRobust method for the elements.
   436  func CopySliceRobust(to, fm any) error {
   437  	tov := reflect.ValueOf(to)
   438  	fmv := reflect.ValueOf(fm)
   439  	tonp := NonPtrValue(tov)
   440  	fmnp := NonPtrValue(fmv)
   441  	totyp := tonp.Type()
   442  	// eltyp := SliceElType(tonp)
   443  	if totyp.Kind() != reflect.Slice {
   444  		err := fmt.Errorf("ki.CopySliceRobust: 'to' is not slice, is: %v", totyp.String())
   445  		log.Println(err)
   446  		return err
   447  	}
   448  	fmtyp := fmnp.Type()
   449  	if fmtyp.Kind() != reflect.Slice {
   450  		err := fmt.Errorf("ki.CopySliceRobust: 'from' is not slice, is: %v", fmtyp.String())
   451  		log.Println(err)
   452  		return err
   453  	}
   454  	fmlen := fmnp.Len()
   455  	if tonp.IsNil() {
   456  		OnePtrValue(tonp).Elem().Set(MakeSlice(totyp, fmlen, fmlen).Elem())
   457  	} else {
   458  		if tonp.Len() > fmlen {
   459  			tonp.SetLen(fmlen)
   460  		}
   461  	}
   462  	for i := 0; i < fmlen; i++ {
   463  		tolen := tonp.Len()
   464  		if i >= tolen {
   465  			SliceNewAt(to, i)
   466  		}
   467  		SetRobust(PtrValue(tonp.Index(i)).Interface(), fmnp.Index(i).Interface())
   468  	}
   469  	return nil
   470  }