-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsorty.go
181 lines (161 loc) · 4.88 KB
/
sorty.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
*
* Copyright 2015 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package sorty
import (
"fmt"
"reflect"
"sort"
)
type sorter struct {
data interface{}
order KeyComps
}
// NewSorter create a new sorter struct which Sort() can be called on.
func NewSorter() *sorter {
return &sorter{}
}
// ByKeys is used to provide a list of string to indicate which keys to sort by and in which
// order. If the key name starts with "-" it will be sorted in Descending order otherwise
// it will be sorted in Ascending order.
func (s *sorter) ByKeys(order []string) *sorter {
keyComps := make(KeyComps, 0)
for _, key := range order {
switch key[0] {
case '-':
keyComps = append(keyComps, KeyComp{key[1:], Descending})
case '+':
keyComps = append(keyComps, KeyComp{key[1:], Ascending})
default:
keyComps = append(keyComps, KeyComp{key, Ascending})
}
}
return s.ByKeyComps(keyComps)
}
// KeyComp struct to provide custom compaitor functions
type KeyComp struct {
Name string
Comp func(interface{}, interface{}) CompareResult
}
type KeyComps []KeyComp
// ByKeyComps is used to provide a list of KeyComp to sort by key with an explicit comparitor.
func (s *sorter) ByKeyComps(keyComps KeyComps) *sorter {
s.order = keyComps
return s
}
// Sort will sort the data provided. The data should be a slice of something.
func (s *sorter) Sort(data interface{}) {
s.data = data
sort.Sort(s)
}
// Len is required to implement sort.Interface
func (s *sorter) Len() int {
return reflect.ValueOf(s.data).Len()
}
// Swap is required to implement sort.Interface
func (s *sorter) Swap(i, j int) {
if i > j {
i, j = j, i
}
arr := reflect.ValueOf(s.data)
tmp := arr.Index(i).Interface()
arr.Index(i).Set(arr.Index(j))
arr.Index(j).Set(reflect.ValueOf(tmp))
}
// Less is required to implement sort.Interface
func (s *sorter) Less(i, j int) bool {
arr := reflect.ValueOf(s.data)
a := reflect.ValueOf(arr.Index(i).Interface())
b := reflect.ValueOf(arr.Index(j).Interface())
if a.Kind() != reflect.Map {
iface := a.Interface()
panic(fmt.Sprintf("[A] Kind: %s, Expected a map, but got a %T for %v", a.Kind(), iface, iface))
}
if b.Kind() != reflect.Map {
iface := b.Interface()
panic(fmt.Sprintf("[B] Kind: %s, Expected a map, but got a %T for %v", b.Kind(), iface, iface))
}
for i := 0; i < len(s.order); i += 1 {
keyComp := s.order[i]
af := a.MapIndex(reflect.ValueOf(keyComp.Name)).Interface()
bf := b.MapIndex(reflect.ValueOf(keyComp.Name)).Interface()
switch keyComp.Comp(af, bf) {
case LESSER:
return true
case GREATER:
return false
}
}
return true
}
type CompareResult int8
const (
LESSER CompareResult = -1 + iota
EQUAL
GREATER
)
func Ascending(a, b interface{}) CompareResult {
switch Descending(a, b) {
case LESSER:
return GREATER
case GREATER:
return LESSER
default:
return EQUAL
}
}
func Descending(a, b interface{}) CompareResult {
if a == b {
return EQUAL
}
switch a.(type) {
case string:
return lg(a.(string) > b.(string))
case int:
return lg(a.(int) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(int))
case int8:
return lg(a.(int8) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(int8))
case int16:
return lg(a.(int16) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(int16))
case int32:
return lg(a.(int32) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(int32))
case int64:
return lg(a.(int64) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(int64))
case uint:
return lg(a.(uint) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(uint))
case uint8:
return lg(a.(uint8) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(uint8))
case uint16:
return lg(a.(uint16) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(uint16))
case uint32:
return lg(a.(uint32) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(uint32))
case uint64:
return lg(a.(uint64) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(uint64))
case float32:
return lg(a.(float32) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(float32))
case float64:
return lg(a.(float64) > reflect.ValueOf(b).Convert(reflect.TypeOf(a)).Interface().(float64))
default:
panic(fmt.Sprintf("dont know how to compare: %T", a))
}
}
func lg(b bool) CompareResult {
if b {
return LESSER
}
return GREATER
}