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 }