goki.dev/laser@v0.1.34/maps.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 maps, in the reflect 17 // system 18 19 // MakeMap makes a map that is actually addressable, getting around the hidden 20 // interface{} that reflect.MakeMap makes, by calling UnhideIfaceValue (from ptrs.go) 21 func MakeMap(typ reflect.Type) reflect.Value { 22 return UnhideAnyValue(reflect.MakeMap(typ)) 23 } 24 25 // MapValueType returns the type of the value for the given map (which can be 26 // a pointer to a map or a direct map) -- just Elem() of map type, but using 27 // this function makes it more explicit what is going on. 28 func MapValueType(mp any) reflect.Type { 29 return NonPtrType(reflect.TypeOf(mp)).Elem() 30 } 31 32 // MapKeyType returns the type of the key for the given map (which can be a 33 // pointer to a map or a direct map) -- just Key() of map type, but using 34 // this function makes it more explicit what is going on. 35 func MapKeyType(mp any) reflect.Type { 36 return NonPtrType(reflect.TypeOf(mp)).Key() 37 } 38 39 // MapElsValueFun calls a function on all the "basic" elements of given map -- 40 // iterates over maps within maps (but not structs, slices within maps). 41 func MapElsValueFun(mp any, fun func(mp any, typ reflect.Type, key, val reflect.Value) bool) bool { 42 vv := reflect.ValueOf(mp) 43 if mp == nil { 44 log.Printf("laser.MapElsValueFun: must pass a non-nil pointer to the map: %v\n", mp) 45 return false 46 } 47 v := NonPtrValue(vv) 48 if !v.IsValid() { 49 return true 50 } 51 typ := v.Type() 52 if typ.Kind() != reflect.Map { 53 log.Printf("laser.MapElsValueFun: non-pointer type is not a map: %v\n", typ.String()) 54 return false 55 } 56 rval := true 57 keys := v.MapKeys() 58 for _, key := range keys { 59 val := v.MapIndex(key) 60 vali := val.Interface() 61 // vt := val.Type() 62 vt := reflect.TypeOf(vali) 63 // fmt.Printf("key %v val %v kind: %v\n", key, val, vt.Kind()) 64 if vt.Kind() == reflect.Map { 65 rval = MapElsValueFun(vali, fun) 66 if !rval { 67 break 68 } 69 // } else if vt.Kind() == reflect.Struct { // todo 70 // rval = MapElsValueFun(vali, fun) 71 // if !rval { 72 // break 73 // } 74 } else { 75 rval = fun(vali, typ, key, val) 76 if !rval { 77 break 78 } 79 } 80 } 81 return rval 82 } 83 84 // MapElsN returns number of elemental fields in given map type 85 func MapElsN(mp any) int { 86 n := 0 87 falseErr := MapElsValueFun(mp, func(mp any, typ reflect.Type, key, val reflect.Value) bool { 88 n++ 89 return true 90 }) 91 if falseErr == false { 92 return 0 93 } 94 return n 95 } 96 97 // MapStructElsValueFun calls a function on all the "basic" elements of given 98 // map or struct -- iterates over maps within maps and fields within structs 99 func MapStructElsValueFun(mp any, fun func(mp any, typ reflect.Type, val reflect.Value) bool) bool { 100 vv := reflect.ValueOf(mp) 101 if mp == nil { 102 log.Printf("laser.MapElsValueFun: must pass a non-nil pointer to the map: %v\n", mp) 103 return false 104 } 105 v := NonPtrValue(vv) 106 if !v.IsValid() { 107 return true 108 } 109 typ := v.Type() 110 vk := typ.Kind() 111 rval := true 112 switch vk { 113 case reflect.Map: 114 keys := v.MapKeys() 115 for _, key := range keys { 116 val := v.MapIndex(key) 117 vali := val.Interface() 118 if AnyIsNil(vali) { 119 continue 120 } 121 vt := reflect.TypeOf(vali) 122 if vt == nil { 123 continue 124 } 125 vtk := vt.Kind() 126 switch vtk { 127 case reflect.Map: 128 rval = MapStructElsValueFun(vali, fun) 129 if !rval { 130 break 131 } 132 case reflect.Struct: 133 rval = MapStructElsValueFun(vali, fun) 134 if !rval { 135 break 136 } 137 default: 138 rval = fun(vali, typ, val) 139 if !rval { 140 break 141 } 142 } 143 } 144 case reflect.Struct: 145 for i := 0; i < typ.NumField(); i++ { 146 f := typ.Field(i) 147 vf := v.Field(i) 148 if !vf.CanInterface() { 149 continue 150 } 151 vfi := vf.Interface() 152 if vfi == mp { 153 continue 154 } 155 vtk := f.Type.Kind() 156 switch vtk { 157 case reflect.Map: 158 rval = MapStructElsValueFun(vfi, fun) 159 if !rval { 160 break 161 } 162 case reflect.Struct: 163 rval = MapStructElsValueFun(vfi, fun) 164 if !rval { 165 break 166 } 167 default: 168 rval = fun(vfi, typ, vf) 169 if !rval { 170 break 171 } 172 } 173 } 174 default: 175 log.Printf("laser.MapStructElsValueFun: non-pointer type is not a map or struct: %v\n", typ.String()) 176 return false 177 } 178 return rval 179 } 180 181 // MapStructElsN returns number of elemental fields in given map / struct types 182 func MapStructElsN(mp any) int { 183 n := 0 184 falseErr := MapStructElsValueFun(mp, func(mp any, typ reflect.Type, val reflect.Value) bool { 185 n++ 186 return true 187 }) 188 if falseErr == false { 189 return 0 190 } 191 return n 192 } 193 194 // MapAdd adds a new blank entry to the map 195 func MapAdd(mv any) { 196 mpv := reflect.ValueOf(mv) 197 mpvnp := NonPtrValue(mpv) 198 mvtyp := mpvnp.Type() 199 valtyp := MapValueType(mv) 200 if valtyp.Kind() == reflect.Interface && valtyp.String() == "interface {}" { 201 str := "" 202 valtyp = reflect.TypeOf(str) 203 } 204 nkey := reflect.New(MapKeyType(mv)) 205 nval := reflect.New(valtyp) 206 if mpvnp.IsNil() { // make a new map 207 nmp := MakeMap(mvtyp) 208 mpv.Elem().Set(nmp.Elem()) 209 mpvnp = NonPtrValue(mpv) 210 } 211 mpvnp.SetMapIndex(nkey.Elem(), nval.Elem()) 212 } 213 214 // MapDelete deletes a key-value from the map (set key to a zero value) 215 func MapDelete(mv any, key any) { 216 mpv := reflect.ValueOf(mv) 217 mpvnp := NonPtrValue(mpv) 218 mpvnp.SetMapIndex(reflect.ValueOf(key), reflect.Value{}) // delete 219 } 220 221 // MapDeleteValue deletes a key-value from the map (set key to a zero value) 222 // -- key is already a reflect.Value 223 func MapDeleteValue(mv any, key reflect.Value) { 224 mpv := reflect.ValueOf(mv) 225 mpvnp := NonPtrValue(mpv) 226 mpvnp.SetMapIndex(key, reflect.Value{}) // delete 227 } 228 229 // MapDeleteAll deletes everything from map 230 func MapDeleteAll(mv any) { 231 mpv := reflect.ValueOf(mv) 232 mpvnp := NonPtrValue(mpv) 233 if mpvnp.Len() == 0 { 234 return 235 } 236 itr := mpvnp.MapRange() 237 for itr.Next() { 238 mpvnp.SetMapIndex(itr.Key(), reflect.Value{}) // delete 239 } 240 } 241 242 // MapSort sorts keys of map either by key or by value, returns those keys as 243 // a slice of reflect.Value, as returned by reflect.Value.MapKeys() method 244 func MapSort(mp any, byKey, ascending bool) []reflect.Value { 245 mpv := reflect.ValueOf(mp) 246 mpvnp := NonPtrValue(mpv) 247 keys := mpvnp.MapKeys() // note: this is a slice of reflect.Value! 248 if byKey { 249 ValueSliceSort(keys, ascending) 250 } else { 251 MapValueSort(mpvnp, keys, ascending) 252 } 253 return keys 254 } 255 256 // MapValueSort sorts keys of map by values 257 func MapValueSort(mpvnp reflect.Value, keys []reflect.Value, ascending bool) error { 258 if len(keys) == 0 { 259 return nil 260 } 261 keyval := keys[0] 262 felval := mpvnp.MapIndex(keyval) 263 eltyp := felval.Type() 264 elnptyp := NonPtrType(eltyp) 265 vk := elnptyp.Kind() 266 elval := OnePtrValue(felval) 267 elif := elval.Interface() 268 269 // try all the numeric types first! 270 271 switch { 272 case vk >= reflect.Int && vk <= reflect.Int64: 273 sort.Slice(keys, func(i, j int) bool { 274 iv := NonPtrValue(mpvnp.MapIndex(keys[i])).Int() 275 jv := NonPtrValue(mpvnp.MapIndex(keys[j])).Int() 276 if ascending { 277 return iv < jv 278 } 279 return iv > jv 280 }) 281 return nil 282 case vk >= reflect.Uint && vk <= reflect.Uint64: 283 sort.Slice(keys, func(i, j int) bool { 284 iv := NonPtrValue(mpvnp.MapIndex(keys[i])).Uint() 285 jv := NonPtrValue(mpvnp.MapIndex(keys[j])).Uint() 286 if ascending { 287 return iv < jv 288 } 289 return iv > jv 290 }) 291 return nil 292 case vk >= reflect.Float32 && vk <= reflect.Float64: 293 sort.Slice(keys, func(i, j int) bool { 294 iv := NonPtrValue(mpvnp.MapIndex(keys[i])).Float() 295 jv := NonPtrValue(mpvnp.MapIndex(keys[j])).Float() 296 if ascending { 297 return iv < jv 298 } 299 return iv > jv 300 }) 301 return nil 302 case vk == reflect.Struct && ShortTypeName(elnptyp) == "time.Time": 303 sort.Slice(keys, func(i, j int) bool { 304 iv := NonPtrValue(mpvnp.MapIndex(keys[i])).Interface().(time.Time) 305 jv := NonPtrValue(mpvnp.MapIndex(keys[j])).Interface().(time.Time) 306 if ascending { 307 return iv.Before(jv) 308 } 309 return jv.Before(iv) 310 }) 311 } 312 313 // this stringer case will likely pick up most of the rest 314 switch elif.(type) { 315 case fmt.Stringer: 316 sort.Slice(keys, func(i, j int) bool { 317 iv := NonPtrValue(mpvnp.MapIndex(keys[i])).Interface().(fmt.Stringer).String() 318 jv := NonPtrValue(mpvnp.MapIndex(keys[j])).Interface().(fmt.Stringer).String() 319 if ascending { 320 return iv < jv 321 } 322 return iv > jv 323 }) 324 return nil 325 } 326 327 // last resort! 328 switch { 329 case vk == reflect.String: 330 sort.Slice(keys, func(i, j int) bool { 331 iv := NonPtrValue(mpvnp.MapIndex(keys[i])).String() 332 jv := NonPtrValue(mpvnp.MapIndex(keys[j])).String() 333 if ascending { 334 return strings.ToLower(iv) < strings.ToLower(jv) 335 } 336 return strings.ToLower(iv) > strings.ToLower(jv) 337 }) 338 return nil 339 } 340 341 err := fmt.Errorf("MapValueSort: unable to sort elements of type: %v", eltyp.String()) 342 log.Println(err) 343 return err 344 } 345 346 // SetMapRobust robustly sets a map value using reflect.Value representations 347 // of the map, key, and value elements, ensuring that the proper types are 348 // used for the key and value elements using sensible conversions. 349 // map value must be a valid map value -- that is not checked. 350 func SetMapRobust(mp, ky, val reflect.Value) bool { 351 mtyp := mp.Type() 352 if mtyp.Kind() != reflect.Map { 353 log.Printf("laser.SetMapRobust: map arg is not map, is: %v\n", mtyp.String()) 354 return false 355 } 356 if !mp.CanSet() { 357 log.Printf("laser.SetMapRobust: map arg is not settable: %v\n", mtyp.String()) 358 return false 359 } 360 ktyp := mtyp.Key() 361 etyp := mtyp.Elem() 362 if etyp.Kind() == val.Kind() && ky.Kind() == ktyp.Kind() { 363 mp.SetMapIndex(ky, val) 364 return true 365 } 366 if ky.Kind() == ktyp.Kind() { 367 mp.SetMapIndex(ky, val.Convert(etyp)) 368 return true 369 } 370 if etyp.Kind() == val.Kind() { 371 mp.SetMapIndex(ky.Convert(ktyp), val) 372 return true 373 } 374 mp.SetMapIndex(ky.Convert(ktyp), val.Convert(etyp)) 375 return true 376 } 377 378 // CopyMapRobust robustly copies maps using SetRobust method for the elements. 379 func CopyMapRobust(to, fm any) error { 380 tov := reflect.ValueOf(to) 381 fmv := reflect.ValueOf(fm) 382 tonp := NonPtrValue(tov) 383 fmnp := NonPtrValue(fmv) 384 totyp := tonp.Type() 385 if totyp.Kind() != reflect.Map { 386 err := fmt.Errorf("laser.CopyMapRobust: 'to' is not map, is: %v", totyp.String()) 387 log.Println(err) 388 return err 389 } 390 fmtyp := fmnp.Type() 391 if fmtyp.Kind() != reflect.Map { 392 err := fmt.Errorf("laser.CopyMapRobust: 'from' is not map, is: %v", fmtyp.String()) 393 log.Println(err) 394 return err 395 } 396 if tonp.IsNil() { 397 OnePtrValue(tov).Elem().Set(MakeMap(totyp).Elem()) 398 } else { 399 MapDeleteAll(to) 400 } 401 if fmnp.Len() == 0 { 402 return nil 403 } 404 eltyp := SliceElType(to) 405 itr := fmnp.MapRange() 406 for itr.Next() { 407 tonp.SetMapIndex(itr.Key(), CloneToType(eltyp, itr.Value().Interface()).Elem()) 408 } 409 return nil 410 }