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 }