-
Notifications
You must be signed in to change notification settings - Fork 1
/
slice.go
161 lines (143 loc) · 4.15 KB
/
slice.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
package tricks
import "reflect"
type TrickSlice reflect.Value
var (
typeTrickSlice = reflect.TypeOf((*TrickSlice)(nil)).Elem() // TrickSlice
typeInterface = reflect.TypeOf((*interface{})(nil)).Elem() // interface{}
)
func Slice(sliceOrElements ...interface{}) TrickSlice {
s := reflect.ValueOf(sliceOrElements) // []interface{}
if len(sliceOrElements) == 0 {
return TrickSlice(s)
}
if len(sliceOrElements) == 1 {
v := reflect.ValueOf(sliceOrElements[0])
if !v.IsValid() { // nil
return TrickSlice(s)
}
if v.Kind() == reflect.Slice { // TrickSlice is a Kind of reflect.Struct
return TrickSlice(v)
}
if v.Type() == typeTrickSlice {
return sliceOrElements[0].(TrickSlice)
}
}
// Try to make a typed slice from variadic args. First identify the type.
var typ reflect.Type
for i := 0; i < len(sliceOrElements); i++ {
el := reflect.ValueOf(sliceOrElements[i])
if el.IsValid() {
switch typ {
case nil:
typ = el.Type()
if typ == typeInterface {
return TrickSlice(s)
}
case el.Type():
default: // mixed types; fall back to []interface{}
return TrickSlice(s)
}
}
}
if typ == nil { // no IsValid (non-nil) values found
return TrickSlice(s)
}
// Now make the slice.
sliceTyp := reflect.SliceOf(typ)
slice := reflect.MakeSlice(sliceTyp, len(sliceOrElements), len(sliceOrElements))
for i := 0; i < len(sliceOrElements); i++ {
el := reflect.ValueOf(sliceOrElements[i])
if el.IsValid() {
slice.Index(i).Set(el)
}
}
return TrickSlice(slice)
}
func (ts TrickSlice) Value() interface{} {
return reflect.Value(ts).Interface()
}
// Copy returns a new slice containing the same values.
func (ts TrickSlice) Copy() TrickSlice {
in := reflect.Value(ts)
out := reflect.MakeSlice(in.Type(), in.Len(), in.Len()) // TODO: v.Cap()?
reflect.Copy(out, in)
return TrickSlice(out)
}
// Len returns the length of the slice.
func (ts TrickSlice) Len() int {
return reflect.Value(ts).Len()
}
// IsEmpty returns true if the slice has no length, else false.
func (ts TrickSlice) IsEmpty() bool {
return ts.Len() == 0
}
// First reslices to only include the first n elements. If n > len(slice), it
// reslices to include all elements. In both cases, cap() of the new slice is
// set to equal its length.
func (ts TrickSlice) First(n int) TrickSlice {
v := reflect.Value(ts)
if n > v.Len() {
n = v.Len()
}
return TrickSlice(v.Slice3(0, n, n))
}
// Last reslices to only include the last n elements. If n > len(slice), it
// reslices to include all elements. In both cases, cap() of the new slice is
// set to equal its length.
func (ts TrickSlice) Last(n int) TrickSlice {
v := reflect.Value(ts)
if n > v.Len() {
n = v.Len()
}
return TrickSlice(v.Slice3(v.Len()-n, v.Len(), v.Len()))
}
// Flatten returns a new slice of values, recursively extracting the elements
// from any nested slices. This new slice tries to take on the type of the first
// non-slice element encountered. If the elements are of mixed types, it falls
// back to []interface{}. nil values are treated as zeroes of the common type.
func (ts TrickSlice) Flatten() TrickSlice {
in := reflect.Value(ts)
var typ reflect.Type
var vals []reflect.Value
var extract, extractSlice func(reflect.Value)
extract = func(el reflect.Value) {
if el.Kind() == reflect.Slice {
extractSlice(el)
return
}
if el.IsValid() {
switch el.Type() {
case typeTrickSlice:
extractSlice(reflect.Value(el.Interface().(TrickSlice)))
return
case typeInterface:
extract(reflect.ValueOf(el.Interface()))
return
default:
if typ == nil {
typ = el.Type()
} else if typ != typeInterface && typ != el.Type() {
typ = typeInterface // fall back to []interface{}
}
}
}
vals = append(vals, el)
}
extractSlice = func(slice reflect.Value) {
// Invariant: slice.Type().Kind() == reflect.Slice
for i := 0; i < slice.Len(); i++ {
extract(slice.Index(i))
}
}
extractSlice(in)
if typ == nil { // no IsValid (non-nil) values found
typ = typeInterface
}
out := reflect.MakeSlice(reflect.SliceOf(typ), len(vals), len(vals))
for i := 0; i < out.Len(); i++ {
if vals[i].IsValid() {
out.Index(i).Set(vals[i])
}
}
return TrickSlice(out)
}