github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/coll/coll.go (about) 1 // Package coll contains functions to help manipulate and query collections of 2 // data, like slices/arrays and maps. 3 // 4 // For the functions that return an array, a []interface{} is returned, 5 // regardless of whether or not the input was a different type. 6 package coll 7 8 import ( 9 "fmt" 10 "reflect" 11 "sort" 12 13 "github.com/hairyhenderson/gomplate/v4/conv" 14 iconv "github.com/hairyhenderson/gomplate/v4/internal/conv" 15 ) 16 17 // Slice creates a slice from a bunch of arguments 18 func Slice(args ...interface{}) []interface{} { 19 return args 20 } 21 22 // Has determines whether or not a given object has a property with the given key 23 func Has(in interface{}, key interface{}) bool { 24 av := reflect.ValueOf(in) 25 26 switch av.Kind() { 27 case reflect.Map: 28 kv := reflect.ValueOf(key) 29 return av.MapIndex(kv).IsValid() 30 case reflect.Slice, reflect.Array: 31 l := av.Len() 32 for i := 0; i < l; i++ { 33 v := av.Index(i).Interface() 34 if reflect.DeepEqual(v, key) { 35 return true 36 } 37 } 38 } 39 40 return false 41 } 42 43 // Dict is a convenience function that creates a map with string keys. 44 // Provide arguments as key/value pairs. If an odd number of arguments 45 // is provided, the last is used as the key, and an empty string is 46 // set as the value. 47 // All keys are converted to strings, regardless of input type. 48 func Dict(v ...interface{}) (map[string]interface{}, error) { 49 dict := map[string]interface{}{} 50 lenv := len(v) 51 for i := 0; i < lenv; i += 2 { 52 key := conv.ToString(v[i]) 53 if i+1 >= lenv { 54 dict[key] = "" 55 continue 56 } 57 dict[key] = v[i+1] 58 } 59 return dict, nil 60 } 61 62 // Keys returns the list of keys in one or more maps. The returned list of keys 63 // is ordered by map, each in sorted key order. 64 func Keys(in ...map[string]interface{}) ([]string, error) { 65 if len(in) == 0 { 66 return nil, fmt.Errorf("need at least one argument") 67 } 68 keys := []string{} 69 for _, m := range in { 70 k, _ := splitMap(m) 71 keys = append(keys, k...) 72 } 73 return keys, nil 74 } 75 76 func splitMap(m map[string]interface{}) ([]string, []interface{}) { 77 keys := make([]string, len(m)) 78 values := make([]interface{}, len(m)) 79 i := 0 80 for k := range m { 81 keys[i] = k 82 i++ 83 } 84 sort.Strings(keys) 85 for i, k := range keys { 86 values[i] = m[k] 87 } 88 return keys, values 89 } 90 91 // Values returns the list of values in one or more maps. The returned list of values 92 // is ordered by map, each in sorted key order. If the Keys function is called with 93 // the same arguments, the key/value mappings will be maintained. 94 func Values(in ...map[string]interface{}) ([]interface{}, error) { 95 if len(in) == 0 { 96 return nil, fmt.Errorf("need at least one argument") 97 } 98 values := []interface{}{} 99 for _, m := range in { 100 _, v := splitMap(m) 101 values = append(values, v...) 102 } 103 return values, nil 104 } 105 106 // Append v to the end of list. No matter what type of input slice or array list is, a new []interface{} is always returned. 107 func Append(v interface{}, list interface{}) ([]interface{}, error) { 108 l, err := iconv.InterfaceSlice(list) 109 if err != nil { 110 return nil, err 111 } 112 113 return append(l, v), nil 114 } 115 116 // Prepend v to the beginning of list. No matter what type of input slice or array list is, a new []interface{} is always returned. 117 func Prepend(v interface{}, list interface{}) ([]interface{}, error) { 118 l, err := iconv.InterfaceSlice(list) 119 if err != nil { 120 return nil, err 121 } 122 123 return append([]interface{}{v}, l...), nil 124 } 125 126 // Uniq finds the unique values within list. No matter what type of input slice or array list is, a new []interface{} is always returned. 127 func Uniq(list interface{}) ([]interface{}, error) { 128 l, err := iconv.InterfaceSlice(list) 129 if err != nil { 130 return nil, err 131 } 132 133 out := []interface{}{} 134 for _, v := range l { 135 if !Has(out, v) { 136 out = append(out, v) 137 } 138 } 139 return out, nil 140 } 141 142 // Reverse the list. No matter what type of input slice or array list is, a new []interface{} is always returned. 143 func Reverse(list interface{}) ([]interface{}, error) { 144 l, err := iconv.InterfaceSlice(list) 145 if err != nil { 146 return nil, err 147 } 148 149 // nifty trick from https://github.com/golang/go/wiki/SliceTricks#reversing 150 for left, right := 0, len(l)-1; left < right; left, right = left+1, right-1 { 151 l[left], l[right] = l[right], l[left] 152 } 153 return l, nil 154 } 155 156 // Merge source maps (srcs) into dst. Precedence is in left-to-right order, with 157 // the left-most values taking precedence over the right-most. 158 func Merge(dst map[string]interface{}, srcs ...map[string]interface{}) (map[string]interface{}, error) { 159 for _, src := range srcs { 160 dst = mergeValues(src, dst) 161 } 162 return dst, nil 163 } 164 165 // returns whether or not a contains v 166 func contains(v string, a []string) bool { 167 for _, n := range a { 168 if n == v { 169 return true 170 } 171 } 172 return false 173 } 174 175 // Omit returns a new map without any entries that have the 176 // given keys (inverse of Pick). 177 func Omit(in map[string]interface{}, keys ...string) map[string]interface{} { 178 out := map[string]interface{}{} 179 for k, v := range in { 180 if !contains(k, keys) { 181 out[k] = v 182 } 183 } 184 return out 185 } 186 187 // Pick returns a new map with any entries that have the 188 // given keys (inverse of Omit). 189 func Pick(in map[string]interface{}, keys ...string) map[string]interface{} { 190 out := map[string]interface{}{} 191 for k, v := range in { 192 if contains(k, keys) { 193 out[k] = v 194 } 195 } 196 return out 197 } 198 199 func copyMap(m map[string]interface{}) map[string]interface{} { 200 n := map[string]interface{}{} 201 for k, v := range m { 202 n[k] = v 203 } 204 return n 205 } 206 207 // Merges a default and override map 208 func mergeValues(d map[string]interface{}, o map[string]interface{}) map[string]interface{} { 209 def := copyMap(d) 210 over := copyMap(o) 211 for k, v := range over { 212 // If the key doesn't exist already, then just set the key to that value 213 if _, exists := def[k]; !exists { 214 def[k] = v 215 continue 216 } 217 nextMap, ok := v.(map[string]interface{}) 218 // If it isn't another map, overwrite the value 219 if !ok { 220 def[k] = v 221 continue 222 } 223 // Edge case: If the key exists in the default, but isn't a map 224 defMap, isMap := def[k].(map[string]interface{}) 225 // If the override map has a map for this key, prefer it 226 if !isMap { 227 def[k] = v 228 continue 229 } 230 // If we got to this point, it is a map in both, so merge them 231 def[k] = mergeValues(defMap, nextMap) 232 } 233 return def 234 } 235 236 // Sort a given array or slice. Uses natural sort order if possible. If a 237 // non-empty key is given and the list elements are maps, this will attempt to 238 // sort by the values of those entries. 239 // 240 // Does not modify the input list. 241 func Sort(key string, list interface{}) (out []interface{}, err error) { 242 if list == nil { 243 return nil, nil 244 } 245 246 ia, err := iconv.InterfaceSlice(list) 247 if err != nil { 248 return nil, err 249 } 250 // if the types are all the same, we can sort the slice 251 if sameTypes(ia) { 252 s := make([]interface{}, len(ia)) 253 // make a copy so the original is unmodified 254 copy(s, ia) 255 sort.SliceStable(s, func(i, j int) bool { 256 return lessThan(key)(s[i], s[j]) 257 }) 258 return s, nil 259 } 260 return ia, nil 261 } 262 263 // lessThan - compare two values of the same type 264 func lessThan(key string) func(left, right interface{}) bool { 265 return func(left, right interface{}) bool { 266 val := reflect.Indirect(reflect.ValueOf(left)) 267 rval := reflect.Indirect(reflect.ValueOf(right)) 268 switch val.Kind() { 269 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 270 return val.Int() < rval.Int() 271 case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: 272 return val.Uint() < rval.Uint() 273 case reflect.Float32, reflect.Float64: 274 return val.Float() < rval.Float() 275 case reflect.String: 276 return val.String() < rval.String() 277 case reflect.MapOf( 278 reflect.TypeOf(reflect.String), 279 reflect.TypeOf(reflect.Interface), 280 ).Kind(): 281 kval := reflect.ValueOf(key) 282 if !val.MapIndex(kval).IsValid() { 283 return false 284 } 285 newleft := val.MapIndex(kval).Interface() 286 newright := rval.MapIndex(kval).Interface() 287 return lessThan("")(newleft, newright) 288 case reflect.Struct: 289 if !val.FieldByName(key).IsValid() { 290 return false 291 } 292 newleft := val.FieldByName(key).Interface() 293 newright := rval.FieldByName(key).Interface() 294 return lessThan("")(newleft, newright) 295 default: 296 // it's not really comparable, so... 297 return false 298 } 299 } 300 } 301 302 func sameTypes(a []interface{}) bool { 303 var t reflect.Type 304 for _, v := range a { 305 if t == nil { 306 t = reflect.TypeOf(v) 307 } 308 if reflect.ValueOf(v).Kind() != t.Kind() { 309 return false 310 } 311 } 312 return true 313 } 314 315 // Flatten a nested array or slice to at most 'depth' levels. Use depth of -1 316 // to completely flatten the input. 317 // Returns a new slice without modifying the input. 318 func Flatten(list interface{}, depth int) ([]interface{}, error) { 319 l, err := iconv.InterfaceSlice(list) 320 if err != nil { 321 return nil, err 322 } 323 if depth == 0 { 324 return l, nil 325 } 326 out := make([]interface{}, 0, len(l)*2) 327 for _, v := range l { 328 s := reflect.ValueOf(v) 329 kind := s.Kind() 330 switch kind { 331 case reflect.Slice, reflect.Array: 332 vl, err := Flatten(v, depth-1) 333 if err != nil { 334 return nil, err 335 } 336 out = append(out, vl...) 337 default: 338 out = append(out, v) 339 } 340 } 341 return out, nil 342 }