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