-
Notifications
You must be signed in to change notification settings - Fork 0
/
slices.go
220 lines (178 loc) · 5.02 KB
/
slices.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package slices
import (
"fmt"
"reflect"
)
// Map maps a slice of T to a slice of S
func Map[T, S any](source []T, f func(T) S) []S {
out := make([]S, len(source))
// avoid allocating a copy of the slice element
for i := range source {
out[i] = f(source[i])
}
return out
}
// FlatMap composes Map and Flatten
func FlatMap[T, S any](source []T, f func(T) []S) []S {
return Flatten(Map(source, f))
}
// Reduce performs a reduction to a single value of the source slice according to the given function
func Reduce[T, S any](source []T, initial S, f func(current S, element T) S) S {
v := initial
for i := range source {
v = f(v, source[i])
}
return v
}
// Filter returns a new slice that only contains elements that match the predicate
func Filter[T any](source []T, predicate func(T) bool) []T {
out := make([]T, 0, cap(source))
for i := range source {
if predicate(source[i]) {
out = append(out, source[i])
}
}
return out
}
// ForEach performs the given function on every element of the slice
func ForEach[T any](source []T, f func(T)) {
for i := range source {
f(source[i])
}
}
// While executes the given function on every element of the slice until the function returns false
func While[T any](source []T, f func(T) bool) {
for i := range source {
if ok := f(source[i]); !ok {
return
}
}
}
// Any tests if any of the elements of the slice match the predicate
func Any[T any](source []T, predicate func(T) bool) bool {
for i := range source {
if predicate(source[i]) {
return true
}
}
return false
}
// All tests if all of the elements of the slice match the predicate
func All[T any](source []T, predicate func(T) bool) bool {
for i := range source {
if !predicate(source[i]) {
return false
}
}
return true
}
// Flatten flattens a slice of slices into a single slice
func Flatten[T any](source [][]T) []T {
var l int
for i := range source {
l += len(source[i])
}
out := make([]T, 0, l)
for i := range source {
for j := range source[i] {
out = append(out, source[i][j])
}
}
return out
}
// Concat concatenates all given slices
func Concat[T any](first []T, other ...[]T) []T {
l := 1 + len(other)
sources := make([][]T, 0, l)
sources = append(sources, first)
sources = append(sources, other...)
return Flatten(sources)
}
// Expand creates a slice by executing the generator function count times
func Expand[T any](generator func(idx int) T, count int) []T {
out := make([]T, count)
for i := 0; i < count; i++ {
out[i] = generator(i)
}
return out
}
// Expand2 creates a slice by executing the generator function count times
func Expand2[T any](generator func() T, count int) []T {
out := make([]T, count)
for i := 0; i < count; i++ {
out[i] = generator()
}
return out
}
// Distinct returns a new slice where duplicate entries are removed
func Distinct[T comparable](source []T) []T {
seen := make(map[T]struct{})
out := make([]T, 0, len(source))
for i := range source {
item := source[i]
if _, ok := seen[item]; ok {
continue
}
seen[item] = struct{}{}
out = append(out, item)
}
return out
}
// HasDuplicates returns true if a slice has duplicate entries, false otherwise.
func HasDuplicates[T comparable](source []T) bool {
return len(Distinct(source)) != len(source)
}
// ToMap returns a map from the given slice with keys associated by the lookup function. Panics if strictUniqueness is set, otherwise overrides values for colliding keys.
func ToMap[T1 any, T2 comparable](source []T1, lookup func(T1) T2, strictUniqueness ...bool) map[T2]T1 {
m := make(map[T2]T1, len(source))
strict := false
if len(strictUniqueness) > 0 {
strict = strictUniqueness[0]
}
for i := range source {
key := lookup(source[i])
if strict {
// separate the conditions so the lookup is only done if strict flag is set
if value, ok := m[key]; ok {
panic(fmt.Sprintf("key %v is not unique, points to %v and %v", key, value, source[i]))
}
}
m[key] = source[i]
}
return m
}
// TryCast tries to cast each element of the slice from type T1 to type T2. Elements get filtered out of the cast is unsuccessful.
func TryCast[T1 any, T2 any](source []T1) []T2 {
out := make([]T2, 0, cap(source))
if len(source) == 0 {
return out
}
t2 := reflect.TypeOf(out).Elem()
for i := range source {
if reflect.TypeOf(source[i]).ConvertibleTo(t2) {
out = append(out, reflect.ValueOf(source[i]).Convert(t2).Interface().(T2))
}
}
return out
}
// Reverse returns a new slice in which the elements aree listed in reverse order
func Reverse[T1 any](source []T1) []T1 {
out := make([]T1, 0, cap(source))
for i := len(source) - 1; i >= 0; i-- {
out = append(out, source[i])
}
return out
}
// Last returns the last element of a slice, panics if the slice is empty
func Last[T any](source []T) T {
return source[len(source)-1]
}
// GroupBy returns a map with given items each group into a slice
func GroupBy[T any, K comparable](source []T, fn func(T) K) map[K][]T {
results := make(map[K][]T)
for _, s := range source {
k := fn(s)
results[k] = append(results[k], s)
}
return results
}