goki.dev/laser@v0.1.34/slices.go (about)

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