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  }