slicesx.go (7970B)
1 // Package slicesx provides slice utilities that compliment Go standard 2 // slices package. 3 package slicesx 4 5 import ( 6 "errors" 7 "fmt" 8 "maps" 9 "slices" 10 "sync" 11 12 "git.sr.ht/~jackmordaunt/jackhammer/errorsx" 13 "git.sr.ht/~jackmordaunt/jackhammer/iterx" 14 ) 15 16 // Fanout across all elements of a slice in parallel. 17 // The function receives the index and element. 18 func Fanout[S ~[]E, E any](seq S, fn func(int, E)) { 19 iterx.Fanout2(slices.All(seq), fn) 20 } 21 22 // FanoutErr is like [Fanout] but returns an aggregated error. 23 func FanoutErr[S ~[]E, E any](seq S, fn func(int, E) error) error { 24 return iterx.FanoutErr2(slices.All(seq), fn) 25 } 26 27 // Map transforms a slice by applying a function to each element and returning the result. 28 func Map[S ~[]E, E any, Out any](s S, fn func(E) Out) []Out { 29 out := make([]Out, len(s)) 30 for ii := range s { 31 out[ii] = fn(s[ii]) 32 } 33 return out 34 } 35 36 // MapIndex is like [Map] but provides the iteration index to the transform function. 37 func MapIndex[S ~[]E, E any, Out any](s S, fn func(int, E) Out) []Out { 38 out := make([]Out, len(s)) 39 for ii := range s { 40 out[ii] = fn(ii, s[ii]) 41 } 42 return out 43 } 44 45 // MapSafe is like [Map] but recovers from panics and returns an error instead. 46 func MapSafe[S ~[]E, E any, Out any](s S, fn func(E) Out) (_ []Out, err error) { 47 defer func() { 48 if v := recover(); v != nil { 49 if e, ok := v.(error); ok { 50 err = e 51 } else { 52 err = fmt.Errorf("%v", v) 53 } 54 } 55 }() 56 return Map(s, fn), err 57 } 58 59 // MapIndexSafe is like [MapIndex] but recovers from panics and returns an error instead. 60 func MapIndexSafe[S ~[]E, E any, Out any](s S, fn func(int, E) Out) (_ []Out, err error) { 61 defer func() { 62 if v := recover(); v != nil { 63 if e, ok := v.(error); ok { 64 err = e 65 } else { 66 err = fmt.Errorf("%v", v) 67 } 68 } 69 }() 70 return MapIndex(s, fn), err 71 } 72 73 // ParaMap transforms a slice by applying a function to each element and returning the result. 74 // Transforms are executed concurrently. 75 func ParaMap[S ~[]E, E any, Out any](s S, fn func(E) Out) []Out { 76 out := make([]Out, len(s)) 77 wg := sync.WaitGroup{} 78 for ii := range s { 79 wg.Add(1) 80 go func() { 81 defer wg.Done() 82 out[ii] = fn(s[ii]) 83 }() 84 } 85 wg.Wait() 86 return out 87 } 88 89 // MapErr transforms a slice by applying a fallible function to each element and 90 // returning the result. 91 // 92 // If the function returns an error the map operation completes, returning an 93 // aggregated error for each failed transform. The output list will be short 94 // by the number of failures. Length of the output is not guaranteed to match that 95 // of the input. 96 func MapErr[S ~[]E, E any, Out any](s S, fn func(E) (Out, error)) (out []Out, err error) { 97 out = make([]Out, 0, len(s)) 98 for ii := range s { 99 v, e := fn(s[ii]) 100 if e != nil { 101 err = errors.Join(err, e) 102 } else { 103 out = append(out, v) 104 } 105 } 106 return out, err 107 } 108 109 // ParaMapErr is [MapErr], but all instances of fn run concurrently. 110 func ParaMapErr[S ~[]E, E any, Out any](s S, fn func(E) (Out, error)) (out []Out, err error) { 111 out = make([]Out, len(s)) 112 errs := make([]error, len(s)) 113 wg := sync.WaitGroup{} 114 115 for ii := range s { 116 wg.Add(1) 117 go func() { 118 defer wg.Done() 119 v, e := fn(s[ii]) 120 if e != nil { 121 errs[ii] = e 122 } else { 123 out[ii] = v 124 } 125 }() 126 } 127 128 wg.Wait() 129 130 for ii, err := range errs { 131 if err != nil { 132 out = slices.Delete(out, ii, ii+1) 133 } 134 } 135 136 if len(errs) > 0 { 137 return out, errorsx.PolyError{Errs: errs} 138 } 139 140 return out, nil 141 } 142 143 // For processes a sequence with the given function. 144 func For[S ~[]E, E any](seq S, fn func(int, E)) { 145 iterx.For2(slices.All(seq), fn) 146 } 147 148 // ForErr processes a sequence with the given function. 149 func ForErr[S ~[]E, E any](seq S, fn func(int, E) error) error { 150 return iterx.ForErr2(slices.All(seq), fn) 151 } 152 153 // Generate returns a slice of [n] items produced by [fn]. 154 func Generate[E any](n int, fn func(int) E) []E { 155 out := make([]E, n) 156 for ii := range n { 157 out[ii] = fn(ii) 158 } 159 return out 160 } 161 162 // Repeat returns a slice of [n] items of value [v]. 163 func Repeat[E any](n int, v E) []E { 164 out := make([]E, n) 165 for ii := range n { 166 out[ii] = v 167 } 168 return out 169 } 170 171 // PopFront returns the first element from the slice. 172 func PopFront[S ~[]E, E any](s *S) E { 173 defer func() { (*s) = (*s)[1:] }() 174 return (*s)[0] 175 } 176 177 // PopFrontSafe returns the first element from the slice, or false. 178 func PopFrontSafe[S ~[]E, E any](s *S) (e E, ok bool) { 179 if s == nil { 180 return e, false 181 } 182 if len(*s) == 0 { 183 return e, false 184 } 185 return PopFront(s), true 186 } 187 188 // PopBack returns the last element from the slice. 189 func PopBack[S ~[]E, E any](s *S) E { 190 defer func() { (*s) = (*s)[:len(*s)-1] }() 191 return (*s)[len(*s)-1] 192 } 193 194 // PopBackSafe returns the last element from the slice, or false. 195 func PopBackSafe[S ~[]E, E any](s *S) (e E, ok bool) { 196 if s == nil { 197 return e, false 198 } 199 if len(*s) == 0 { 200 return e, false 201 } 202 return PopBack(s), true 203 } 204 205 // Filter out elements from a slice when the predicate returns false. 206 func Filter[S ~[]E, E any](s S, fn func(E) bool) S { 207 out := make(S, 0, len(s)) 208 for _, v := range s { 209 if fn(v) { 210 out = append(out, v) 211 } 212 } 213 return slices.Clip(out) 214 } 215 216 // FilterMap maps [In] to [Out], ignoring entries that return false. 217 func FilterMap[S ~[]In, In any, Out any](s S, fn func(In) (Out, bool)) []Out { 218 out := make([]Out, 0, len(s)) 219 for _, v := range s { 220 if v, ok := fn(v); ok { 221 out = append(out, v) 222 } 223 } 224 return slices.Clip(out) 225 } 226 227 // TakeRange selects all items between [start] and [end] and removes them from the slice. 228 // Does not bounds check. 229 func TakeRange[S ~[]E, E any](s *S, start, end int) S { 230 taken := (*s)[start:end] 231 *s = slices.Delete(*s, start, end) 232 return taken 233 } 234 235 // TakeRangeSafe selects all items between [start] and [end] and removes them from the slice. 236 // Checks the bounds. 237 func TakeRangeSafe[S ~[]E, E any](s *S, start, end int) (S, bool) { 238 if start < 0 || start >= end || end < 0 || end > len(*s)-1 || start > len(*s)-1 { 239 return nil, false 240 } 241 taken := (*s)[start:end] 242 *s = slices.Delete(*s, start, end) 243 return taken, true 244 } 245 246 // TakeAllFunc selects all items for which [fn] returns true and removes them from the slice. 247 func TakeAllFunc[S ~[]E, E any](s *S, fn func(E) bool) (S, bool) { 248 indexes := []int{} 249 for ii, v := range *s { 250 if fn(v) { 251 indexes = append(indexes, ii) 252 } 253 } 254 if len(indexes) == 0 { 255 return nil, false 256 } 257 out := make(S, 0, len(indexes)) 258 for _, index := range indexes { 259 out = append(out, (*s)[index]) 260 } 261 *s = slices.DeleteFunc(*s, fn) 262 return out, true 263 } 264 265 // Take selects the item and removes it, without bounds checks. 266 func Take[S ~[]E, E any](s *S, index int) E { 267 taken := (*s)[index] 268 *s = slices.Delete(*s, index, index+1) 269 return taken 270 } 271 272 // TakeSafe selects the item and removes it, returning true if found. 273 func TakeSafe[S ~[]E, E any](s *S, index int) (E, bool) { 274 if index < 0 || index > len(*s)-1 { 275 return *new(E), false 276 } 277 taken := (*s)[index] 278 *s = slices.Delete(*s, index, index+1) 279 return taken, true 280 } 281 282 // TakeFunc selects the first item that satisfies [fn] and removes it. 283 func TakeFunc[S ~[]E, E any](s *S, fn func(E) bool) (E, bool) { 284 ii := slices.IndexFunc(*s, fn) 285 if ii < 0 { 286 return *new(E), false 287 } 288 taken := (*s)[ii] 289 *s = slices.Delete(*s, ii, ii+1) 290 return taken, true 291 } 292 293 // Normalize de-duplicates a slices of comparable elements. 294 // Doesn't rely on sorting. 295 func Normalize[S ~[]E, E comparable](s S) S { 296 s = slices.Clone(s) 297 seen := map[E]struct{}{} 298 for _, e := range s { 299 seen[e] = struct{}{} 300 } 301 return slices.Collect(maps.Keys(seen)) 302 } 303 304 // Flatten reduces a 2D slices to a 1D slice. 305 func Flatten[E any](s [][]E) []E { 306 count := 0 307 for _, ss := range s { 308 count += len(ss) 309 } 310 311 out := make([]E, 0, count) 312 313 for _, ss := range s { 314 for _, e := range ss { 315 out = append(out, e) 316 } 317 } 318 319 return out 320 } 321 322 // ToMap converts a slice to a map, using [keyFor] to extract the key from each element. 323 func ToMap[S ~[]E, E any, K comparable](s S, keyFor func(int, E) K) map[K]E { 324 return iterx.ToMap(slices.All(s), keyFor) 325 }