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 }