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 }