-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheach.go
More file actions
158 lines (151 loc) · 5.62 KB
/
each.go
File metadata and controls
158 lines (151 loc) · 5.62 KB
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
// Package each provides per-element slice operations that Go's stdlib
// skipped.
//
// Find, Filter, GroupBy, KeyBy, Partition, Count, and Every all take a
// slice and a predicate or key-function, and return a concrete result
// without mutating the input. Every operation is nil-safe, never panics
// on valid input, and allocates new slices or maps for its output.
//
// This package deliberately does NOT include Map, Reduce, Fold, or
// FlatMap. A two-line for-loop in Go is usually clearer than a lambda,
// and the stdlib has no slices.Map for the same reason: Go's generics
// do not allow method-level type parameters, making idiomatic use of
// a generic Map function awkward.
//
// # Concurrency
//
// All functions are safe to call concurrently on the same input slice
// as long as the input slice is not modified during the call. They
// neither read from nor write to package-level state.
//
// # Panic safety from non-comparable interface values
//
// GroupBy and KeyBy use the key-function's result as a map key. If the
// caller supplies a key function that returns a non-comparable dynamic
// type (e.g., a slice stored in an any), Go's map implementation will
// panic at runtime. each does not recover from these panics — it is the
// caller's responsibility to ensure the key function returns a
// comparable value.
//
// # Nil predicates
//
// A nil predicate or key function will panic with a nil pointer
// dereference on the first element, matching the convention of
// slices.IndexFunc and similar stdlib helpers. Empty or nil input
// slices do not invoke the predicate at all and so are safe even
// with a nil function value.
//
// # Large value types
//
// Find returns T by value, which copies the element. For large value
// types (e.g., structs containing arrays or many fields), callers who
// want to avoid the copy should use []*T instead of []T so Find returns
// a pointer.
//
// For documentation and examples, see https://github.com/bold-minds/each.
package each
// Find returns the first element of s for which pred returns true, and true.
// Returns (zero, false) if no element matches, or if s is nil/empty.
func Find[T any](s []T, pred func(T) bool) (T, bool) {
for _, v := range s {
if pred(v) {
return v, true
}
}
var zero T
return zero, false
}
// Filter returns a new slice containing all elements of s for which pred
// returns true. The input slice is not modified. Returns a non-nil empty
// slice if no elements match or if s is nil/empty.
//
// Filter pre-allocates a result slice with capacity equal to len(s) so
// it never has to grow. For highly selective predicates on very large
// inputs (e.g., 0.1% match rate on a million-element slice) this may
// reserve more memory than strictly necessary; callers who care can
// pass the result through slices.Clip.
func Filter[T any](s []T, pred func(T) bool) []T {
result := make([]T, 0, len(s))
for _, v := range s {
if pred(v) {
result = append(result, v)
}
}
return result
}
// GroupBy groups the elements of s by the key returned by keyFn, returning
// a map from key to a slice of matching elements. Elements within each
// group preserve their relative order from the input slice.
//
// Returns a non-nil empty map for nil or empty input.
//
// Unlike KeyBy, GroupBy deliberately does not pass a size hint to make:
// the number of distinct groups is unknown a priori and hinting len(s)
// would over-allocate for any input that isn't strictly one-per-group.
func GroupBy[T any, K comparable](s []T, keyFn func(T) K) map[K][]T {
result := make(map[K][]T)
for _, v := range s {
k := keyFn(v)
result[k] = append(result[k], v)
}
return result
}
// KeyBy indexes the elements of s by the key returned by keyFn. If two
// elements produce the same key, the later element overwrites the earlier
// (last-wins). Use GroupBy if you need to preserve all elements per key.
//
// Returns a non-nil empty map for nil or empty input.
func KeyBy[T any, K comparable](s []T, keyFn func(T) K) map[K]T {
result := make(map[K]T, len(s))
for _, v := range s {
result[keyFn(v)] = v
}
return result
}
// Partition splits s into two slices: elements for which pred returns
// true, and elements for which it returns false. Both returned slices
// are non-nil, even if empty. Elements preserve their relative order
// from the input within each output slice.
//
// Partition iterates s exactly once and calls pred once per element,
// which is cheaper in CPU than calling Filter twice with opposite
// predicates. It does pre-allocate len(s) capacity for each of the two
// output slices (2×len(s) total reserved capacity) so that neither
// half has to grow; callers with strongly skewed splits on very large
// inputs may prefer two Filter calls or a manual loop if peak memory
// matters more than iteration count.
func Partition[T any](s []T, pred func(T) bool) (matched, unmatched []T) {
matched = make([]T, 0, len(s))
unmatched = make([]T, 0, len(s))
for _, v := range s {
if pred(v) {
matched = append(matched, v)
} else {
unmatched = append(unmatched, v)
}
}
return matched, unmatched
}
// Count returns the number of elements in s for which pred returns true.
// Returns 0 for nil or empty input.
func Count[T any](s []T, pred func(T) bool) int {
n := 0
for _, v := range s {
if pred(v) {
n++
}
}
return n
}
// Every returns true if pred returns true for every element of s, or if
// s is nil/empty (vacuously true). Short-circuits on the first false,
// so pred is called at most once per element and is never called at
// all for nil or empty input.
func Every[T any](s []T, pred func(T) bool) bool {
for _, v := range s {
if !pred(v) {
return false
}
}
return true
}