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  }