-
Notifications
You must be signed in to change notification settings - Fork 0
/
sortmap.go
130 lines (116 loc) · 4.02 KB
/
sortmap.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
// Package sortmap allows for sorting maps by a custom comparator.
// For convenience, functions sorting by keys or values in ascending or descending
// order are provided – these can deal with limited types only, which are:
// bool, all built-in numerical types and string, time.Time.
//
// Functions provided by this package panic when non-map type is passed for sorting
// or, in case of the key/value sorters, the underyling type is not supported.
package sortmap
import (
"fmt"
"reflect"
"sort"
"time"
)
// Item is a key-value pair representing element in the map
type Item struct {
Key, Value interface{}
}
// Less compares two map elements and returns true if x < y
type Less func(x, y Item) bool
// flatmap is a flattened map with a comparator to be used with sort
type flatmap struct {
items []Item
less Less
}
func newFlatMap(m interface{}, less Less) *flatmap {
mv := reflect.ValueOf(m)
keys := mv.MapKeys()
fm := &flatmap{items: make([]Item, len(keys)), less: less}
for n := range keys {
fm.items[n] = Item{keys[n].Interface(), mv.MapIndex(keys[n]).Interface()}
}
return fm
}
func (m *flatmap) Len() int {
return len(m.items)
}
func (m *flatmap) Less(i, j int) bool {
return m.less(m.items[i], m.items[j])
}
func (m *flatmap) Swap(i, j int) {
m.items[i], m.items[j] = m.items[j], m.items[i]
}
// Items is a slice of map elements (key-value pairs)
type Items []Item
// Top returns slice of up to n leading elements
func (r Items) Top(n int) Items {
if n > len(r) {
n = len(r)
}
return r[:n]
}
// ByFunc sorts map using a provided comparator
func ByFunc(m interface{}, c Less) Items {
fm := newFlatMap(m, c)
sort.Sort(fm)
return fm.items
}
// ByKey sorts map by keys in the ascending order
func ByKey(m interface{}) Items {
ls := getLess(reflect.ValueOf(m).Type().Key())
return ByFunc(m, func(x, y Item) bool { return ls(x.Key, y.Key) })
}
// ByKeyDesc sorts map by keys in the descending order
func ByKeyDesc(m interface{}) Items {
ls := getLess(reflect.ValueOf(m).Type().Key())
return ByFunc(m, func(x, y Item) bool { return ls(y.Key, x.Key) })
}
// ByValue sorts map by values in the ascending order
func ByValue(m interface{}) Items {
ls := getLess(reflect.ValueOf(m).Type().Elem())
return ByFunc(m, func(x, y Item) bool { return ls(x.Value, y.Value) })
}
// ByValueDesc sorts map by values in the descending order
func ByValueDesc(m interface{}) Items {
ls := getLess(reflect.ValueOf(m).Type().Elem())
return ByFunc(m, func(x, y Item) bool { return ls(y.Value, x.Value) })
}
// getLess returns default comparator for a type
func getLess(t reflect.Type) (f func(x, y interface{}) bool) {
switch t.Kind() {
case reflect.Bool:
f = func(x, y interface{}) bool { return !x.(bool) && y.(bool) }
case reflect.Int:
f = func(x, y interface{}) bool { return x.(int) < y.(int) }
case reflect.Int8:
f = func(x, y interface{}) bool { return x.(int8) < y.(int8) }
case reflect.Int16:
f = func(x, y interface{}) bool { return x.(int16) < y.(int16) }
case reflect.Int32:
f = func(x, y interface{}) bool { return x.(int32) < y.(int32) }
case reflect.Int64:
f = func(x, y interface{}) bool { return x.(int64) < y.(int64) }
case reflect.Uint:
f = func(x, y interface{}) bool { return x.(uint) < y.(uint) }
case reflect.Uint8:
f = func(x, y interface{}) bool { return x.(uint8) < y.(uint8) }
case reflect.Uint16:
f = func(x, y interface{}) bool { return x.(uint16) < y.(uint16) }
case reflect.Uint32:
f = func(x, y interface{}) bool { return x.(uint32) < y.(uint32) }
case reflect.Uint64:
f = func(x, y interface{}) bool { return x.(uint64) < y.(uint64) }
case reflect.Float32:
f = func(x, y interface{}) bool { return x.(float32) < y.(float32) }
case reflect.Float64:
f = func(x, y interface{}) bool { return x.(float64) < y.(float64) }
case reflect.String:
f = func(x, y interface{}) bool { return x.(string) < y.(string) }
case reflect.TypeOf(time.Time{}).Kind():
f = func(x, y interface{}) bool { return x.(time.Time).Before(y.(time.Time)) }
default:
panic(fmt.Sprintf("sortmap: unsupported type: %s", t))
}
return
}