github.com/shohhei1126/hugo@v0.42.2-0.20180623210752-3d5928889ad7/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/tpl/compare"
    23  	"github.com/spf13/cast"
    24  )
    25  
    26  var comp = compare.New()
    27  
    28  // Sort returns a sorted sequence.
    29  func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, error) {
    30  	if seq == nil {
    31  		return nil, errors.New("sequence must be provided")
    32  	}
    33  
    34  	seqv := reflect.ValueOf(seq)
    35  	seqv, isNil := indirect(seqv)
    36  	if isNil {
    37  		return nil, errors.New("can't iterate over a nil value")
    38  	}
    39  
    40  	switch seqv.Kind() {
    41  	case reflect.Array, reflect.Slice, reflect.Map:
    42  		// ok
    43  	default:
    44  		return nil, errors.New("can't sort " + reflect.ValueOf(seq).Type().String())
    45  	}
    46  
    47  	// Create a list of pairs that will be used to do the sort
    48  	p := pairList{SortAsc: true, SliceType: reflect.SliceOf(seqv.Type().Elem())}
    49  	p.Pairs = make([]pair, seqv.Len())
    50  
    51  	var sortByField string
    52  	for i, l := range args {
    53  		dStr, err := cast.ToStringE(l)
    54  		switch {
    55  		case i == 0 && err != nil:
    56  			sortByField = ""
    57  		case i == 0 && err == nil:
    58  			sortByField = dStr
    59  		case i == 1 && err == nil && dStr == "desc":
    60  			p.SortAsc = false
    61  		case i == 1:
    62  			p.SortAsc = true
    63  		}
    64  	}
    65  	path := strings.Split(strings.Trim(sortByField, "."), ".")
    66  
    67  	switch seqv.Kind() {
    68  	case reflect.Array, reflect.Slice:
    69  		for i := 0; i < seqv.Len(); i++ {
    70  			p.Pairs[i].Value = seqv.Index(i)
    71  			if sortByField == "" || sortByField == "value" {
    72  				p.Pairs[i].Key = p.Pairs[i].Value
    73  			} else {
    74  				v := p.Pairs[i].Value
    75  				var err error
    76  				for _, elemName := range path {
    77  					v, err = evaluateSubElem(v, elemName)
    78  					if err != nil {
    79  						return nil, err
    80  					}
    81  				}
    82  				p.Pairs[i].Key = v
    83  			}
    84  		}
    85  
    86  	case reflect.Map:
    87  		keys := seqv.MapKeys()
    88  		for i := 0; i < seqv.Len(); i++ {
    89  			p.Pairs[i].Value = seqv.MapIndex(keys[i])
    90  			if sortByField == "" {
    91  				p.Pairs[i].Key = keys[i]
    92  			} else if sortByField == "value" {
    93  				p.Pairs[i].Key = p.Pairs[i].Value
    94  			} else {
    95  				v := p.Pairs[i].Value
    96  				var err error
    97  				for _, elemName := range path {
    98  					v, err = evaluateSubElem(v, elemName)
    99  					if err != nil {
   100  						return nil, err
   101  					}
   102  				}
   103  				p.Pairs[i].Key = v
   104  			}
   105  		}
   106  	}
   107  	return p.sort(), nil
   108  }
   109  
   110  // Credit for pair sorting method goes to Andrew Gerrand
   111  // https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
   112  // A data structure to hold a key/value pair.
   113  type pair struct {
   114  	Key   reflect.Value
   115  	Value reflect.Value
   116  }
   117  
   118  // A slice of pairs that implements sort.Interface to sort by Value.
   119  type pairList struct {
   120  	Pairs     []pair
   121  	SortAsc   bool
   122  	SliceType reflect.Type
   123  }
   124  
   125  func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] }
   126  func (p pairList) Len() int      { return len(p.Pairs) }
   127  func (p pairList) Less(i, j int) bool {
   128  	iv := p.Pairs[i].Key
   129  	jv := p.Pairs[j].Key
   130  
   131  	if iv.IsValid() {
   132  		if jv.IsValid() {
   133  			// can only call Interface() on valid reflect Values
   134  			return comp.Lt(iv.Interface(), jv.Interface())
   135  		}
   136  		// if j is invalid, test i against i's zero value
   137  		return comp.Lt(iv.Interface(), reflect.Zero(iv.Type()))
   138  	}
   139  
   140  	if jv.IsValid() {
   141  		// if i is invalid, test j against j's zero value
   142  		return comp.Lt(reflect.Zero(jv.Type()), jv.Interface())
   143  	}
   144  
   145  	return false
   146  }
   147  
   148  // sorts a pairList and returns a slice of sorted values
   149  func (p pairList) sort() interface{} {
   150  	if p.SortAsc {
   151  		sort.Sort(p)
   152  	} else {
   153  		sort.Sort(sort.Reverse(p))
   154  	}
   155  	sorted := reflect.MakeSlice(p.SliceType, len(p.Pairs), len(p.Pairs))
   156  	for i, v := range p.Pairs {
   157  		sorted.Index(i).Set(v.Value)
   158  	}
   159  
   160  	return sorted.Interface()
   161  }