github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/tpl/collections/sort.go (about)

     1  // Copyright 2017 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package collections
    15  
    16  import (
    17  	"errors"
    18  	"reflect"
    19  	"sort"
    20  	"strings"
    21  
    22  	"github.com/gohugoio/hugo/common/maps"
    23  	"github.com/gohugoio/hugo/tpl/compare"
    24  	"github.com/spf13/cast"
    25  )
    26  
    27  var sortComp = compare.New(true)
    28  
    29  // Sort returns a sorted sequence.
    30  func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, error) {
    31  	if seq == nil {
    32  		return nil, errors.New("sequence must be provided")
    33  	}
    34  
    35  	seqv, isNil := indirect(reflect.ValueOf(seq))
    36  	if isNil {
    37  		return nil, errors.New("can't iterate over a nil value")
    38  	}
    39  
    40  	var sliceType reflect.Type
    41  	switch seqv.Kind() {
    42  	case reflect.Array, reflect.Slice:
    43  		sliceType = seqv.Type()
    44  	case reflect.Map:
    45  		sliceType = reflect.SliceOf(seqv.Type().Elem())
    46  	default:
    47  		return nil, errors.New("can't sort " + reflect.ValueOf(seq).Type().String())
    48  	}
    49  
    50  	// Create a list of pairs that will be used to do the sort
    51  	p := pairList{SortAsc: true, SliceType: sliceType}
    52  	p.Pairs = make([]pair, seqv.Len())
    53  
    54  	var sortByField string
    55  	for i, l := range args {
    56  		dStr, err := cast.ToStringE(l)
    57  		switch {
    58  		case i == 0 && err != nil:
    59  			sortByField = ""
    60  		case i == 0 && err == nil:
    61  			sortByField = dStr
    62  		case i == 1 && err == nil && dStr == "desc":
    63  			p.SortAsc = false
    64  		case i == 1:
    65  			p.SortAsc = true
    66  		}
    67  	}
    68  	path := strings.Split(strings.Trim(sortByField, "."), ".")
    69  
    70  	switch seqv.Kind() {
    71  	case reflect.Array, reflect.Slice:
    72  		for i := 0; i < seqv.Len(); i++ {
    73  			p.Pairs[i].Value = seqv.Index(i)
    74  			if sortByField == "" || sortByField == "value" {
    75  				p.Pairs[i].Key = p.Pairs[i].Value
    76  			} else {
    77  				v := p.Pairs[i].Value
    78  				var err error
    79  				for i, elemName := range path {
    80  					v, err = evaluateSubElem(v, elemName)
    81  					if err != nil {
    82  						return nil, err
    83  					}
    84  					if !v.IsValid() {
    85  						continue
    86  					}
    87  					// Special handling of lower cased maps.
    88  					if params, ok := v.Interface().(maps.Params); ok {
    89  						v = reflect.ValueOf(params.Get(path[i+1:]...))
    90  						break
    91  					}
    92  				}
    93  				p.Pairs[i].Key = v
    94  			}
    95  		}
    96  
    97  	case reflect.Map:
    98  		keys := seqv.MapKeys()
    99  		for i := 0; i < seqv.Len(); i++ {
   100  			p.Pairs[i].Value = seqv.MapIndex(keys[i])
   101  
   102  			if sortByField == "" {
   103  				p.Pairs[i].Key = keys[i]
   104  			} else if sortByField == "value" {
   105  				p.Pairs[i].Key = p.Pairs[i].Value
   106  			} else {
   107  				v := p.Pairs[i].Value
   108  				var err error
   109  				for i, elemName := range path {
   110  					v, err = evaluateSubElem(v, elemName)
   111  					if err != nil {
   112  						return nil, err
   113  					}
   114  					if !v.IsValid() {
   115  						continue
   116  					}
   117  					// Special handling of lower cased maps.
   118  					if params, ok := v.Interface().(maps.Params); ok {
   119  						v = reflect.ValueOf(params.Get(path[i+1:]...))
   120  						break
   121  					}
   122  				}
   123  				p.Pairs[i].Key = v
   124  			}
   125  		}
   126  	}
   127  	return p.sort(), nil
   128  }
   129  
   130  // Credit for pair sorting method goes to Andrew Gerrand
   131  // https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
   132  // A data structure to hold a key/value pair.
   133  type pair struct {
   134  	Key   reflect.Value
   135  	Value reflect.Value
   136  }
   137  
   138  // A slice of pairs that implements sort.Interface to sort by Value.
   139  type pairList struct {
   140  	Pairs     []pair
   141  	SortAsc   bool
   142  	SliceType reflect.Type
   143  }
   144  
   145  func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] }
   146  func (p pairList) Len() int      { return len(p.Pairs) }
   147  func (p pairList) Less(i, j int) bool {
   148  	iv := p.Pairs[i].Key
   149  	jv := p.Pairs[j].Key
   150  
   151  	if iv.IsValid() {
   152  		if jv.IsValid() {
   153  			// can only call Interface() on valid reflect Values
   154  			return sortComp.Lt(iv.Interface(), jv.Interface())
   155  		}
   156  
   157  		// if j is invalid, test i against i's zero value
   158  		return sortComp.Lt(iv.Interface(), reflect.Zero(iv.Type()))
   159  	}
   160  
   161  	if jv.IsValid() {
   162  		// if i is invalid, test j against j's zero value
   163  		return sortComp.Lt(reflect.Zero(jv.Type()), jv.Interface())
   164  	}
   165  
   166  	return false
   167  }
   168  
   169  // sorts a pairList and returns a slice of sorted values
   170  func (p pairList) sort() interface{} {
   171  	if p.SortAsc {
   172  		sort.Sort(p)
   173  	} else {
   174  		sort.Sort(sort.Reverse(p))
   175  	}
   176  	sorted := reflect.MakeSlice(p.SliceType, len(p.Pairs), len(p.Pairs))
   177  	for i, v := range p.Pairs {
   178  		sorted.Index(i).Set(v.Value)
   179  	}
   180  
   181  	return sorted.Interface()
   182  }