Skip to content

Adding Error variants: MapErr, FlatMapErr, ReduceErr...#823

Merged
samber merged 43 commits intomasterfrom
feat/error-variants
Mar 2, 2026
Merged

Adding Error variants: MapErr, FlatMapErr, ReduceErr...#823
samber merged 43 commits intomasterfrom
feat/error-variants

Conversation

@samber
Copy link
Owner

@samber samber commented Feb 28, 2026

Full list 👇

from slice.go (13 helpers)

func FilterErr[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) (bool, error)) (Slice, error)
func RejectErr[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) (bool, error)) (Slice, error)
func MapErr[T, R any](collection []T, transform func(item T, index int) (R, error)) ([]R, error)
func FlatMapErr[T, R any](collection []T, transform func(item T, index int) ([]R, error)) ([]R, error)
func ReduceErr[T, R any](collection []T, accumulator func(agg R, item T, index int) (R, error), initial R) (R, error)
func ReduceRightErr[T, R any](collection []T, accumulator func(agg R, item T, index int) (R, error), initial R) (R, error)
func UniqByErr[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) (U, error)) (Slice, error)
func GroupByErr[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) (U, error)) (map[U]Slice, error)
func GroupByMapErr[T any, K comparable, V any](collection []T, transform func(item T) (K, V, error)) (map[K][]V,
error)
func PartitionByErr[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) (K, error)) ([]Slice,
error)
func RepeatByErr[T any](count int, callback func(index int) (T, error)) ([]T, error)
func KeyByErr[K comparable, V any](collection []V, iteratee func(item V) (K, error)) (map[K]V, error)
func CountByErr[T any](collection []T, predicate func(item T) (bool, error)) (int, error)

from find.go (8 helpers)

func FindErr[T any](collection []T, predicate func(item T) (bool, error)) (T, error)
func FindDuplicatesByErr[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) (U, error))
func MinByErr[T any](collection []T, less func(a, b T) (bool, error)) (T, error)
func MinIndexByErr[T any](collection []T, less func(a, b T) (bool, error)) (T, int, error)
func MaxByErr[T any](collection []T, greater func(a, b T) (bool, error)) (T, error)
func MaxIndexByErr[T any](collection []T, greater func(a, b T) (bool, error)) (T, int, error)
func EarliestByErr[T any](collection []T, iteratee func(item T) (time.Time, error)) (T, error)
func LatestByErr[T any](collection []T, iteratee func(item T) (time.Time, error)) (T, error)

from map.go (7 helpers)

func MapKeysErr[K comparable, V any, R comparable](in map[K]V, iteratee func(value V, key K) (R, error)) (map[R]V, error)
func MapValuesErr[K comparable, V, R any](in map[K]V, iteratee func(value V, key K) (R, error)) (map[K]R, error)
func MapEntriesErr[K1 comparable, V1 any, K2 comparable, V2 any](in map[K1]V1, iteratee func(key K1, value V1) (K2, V2, error)) (map[K2]V2, error)
func MapToSliceErr[K comparable, V, R any](in map[K]V, iteratee func(key K, value V) (R, error)) ([]R, error)
func PickByErr[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) (bool, error)) (Map, error)
func OmitByErr[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) (bool, error)) (Map, error)
func FilterKeysErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]K, error)
func FilterValuesErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]V, error)

from math.go (3 helpers)

func SumByErr[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) (R, error)) (R, error)
func ProductByErr[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) (R, error)) (R, error)
func MeanByErr[T any, R constraints.Float | constraints.Integer](collection []T, iteratee func(item T) (R, error)) (R, error)

from intersect.go (1 helper)

func WithoutByErr[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) (K, error), exclude ...K) (Slice, error)

from tuples.go (3 helpers)

func ZipByErr2[A, B, Out any](a []A, b []B, iteratee func(a A, b B) (Out, error)) ([]Out, error)
func UnzipByErr2[In any, A any, B any](items []In, predicate func(In) (a A, b B, err error)) ([]A, []B, error)
func CrossJoinByErr2[A, B, Out any](listA []A, listB []B, transform func(a A, b B) (Out, error)) ([]Out, error)

We don't use errors.Join(...). The helper returns immediately as soon as we get an error.

Notes:

  • No update on mutable sub-package, because it would return a partial result, and I decided to return either a zero value or a full result for the core package. Please tell me what you think.
  • No update on it sub-package because you cannot pass an error in an iter.Seq data structure. See https://github.com/samber/ro for proper error handling.
  • Some helpers for the `parallel package will be added in a different PR
  • Some helpers have no variant, even if they receive an iteratee/transformation callback. This PR is not exhaustive. More to come.
  • The Indexed variants are not included (eg: SumByI and SumByErrI)

Fixes #292 #43 #82 #342 #410 #264 #639

Previous participants to the discussion: @Southclaws @NilsJPWerner @finnigantime @wrouesnel @jeremybeard @amitlicht @simek-m @marlon-wiprud @toong-mineis @rostislaved @genvmoroz @wesdotcool @lmika @wangmir @amitlicht @ei-sugimoto @toong-mineis

Copilot AI review requested due to automatic review settings February 28, 2026 17:47
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds error-propagating variants of a large number of collection utility functions in the lo library. When an iteratee/transform/accumulator function can return an error, the *Err variant stops at the first error and returns it immediately, rather than continuing. The PR covers slice.go, map.go, math.go, find.go, and intersect.go, plus corresponding tests and documentation.

Changes:

  • Adds *Err variants for slice operations: MapErr, FlatMapErr, ReduceErr, ReduceRightErr, UniqByErr, GroupByErr, GroupByMapErr, PartitionByErr, KeyByErr, CountByErr, WithoutByErr
  • Adds *Err variants for map operations: PickByErr, OmitByErr, MapKeysErr, MapValuesErr, MapEntriesErr, MapToSliceErr
  • Adds *Err variants for math/find operations: SumByErr, ProductByErr, MeanByErr, MinByErr, MaxByErr, MinIndexByErr, MaxIndexByErr, EarliestByErr, LatestByErr
  • Adds comprehensive tests, example functions, and documentation (markdown files + README updates) for all new variants

Reviewed changes

Copilot reviewed 64 out of 64 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
slice.go Adds 11 new *Err slice utility functions
map.go Adds 6 new *Err map utility functions
math.go Adds SumByErr, ProductByErr, MeanByErr
find.go Adds MinByErr, MaxByErr, MinIndexByErr, MaxIndexByErr, EarliestByErr, LatestByErr
intersect.go Adds WithoutByErr
slice_test.go Tests for all new slice *Err functions
map_test.go Tests for all new map *Err functions (with non-determinism bugs)
math_test.go Tests for SumByErr, ProductByErr, MeanByErr
find_test.go Tests for all new find *Err functions
intersect_test.go Tests for WithoutByErr
lo_example_test.go Example functions for new API
README.md Documentation examples added; contains invalid Go syntax in WithoutByErr example
docs/data/core-maperr.md New doc file with stray trailing text
docs/data/core-*.md (others) New or updated documentation files

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link

codecov bot commented Feb 28, 2026

Codecov Report

❌ Patch coverage is 94.38503% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.43%. Comparing base (578bfa9) to head (bbe6dc5).
⚠️ Report is 45 commits behind head on master.

Files with missing lines Patch % Lines
tuples.go 90.81% 18 Missing and 18 partials ⚠️
find.go 95.31% 4 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #823      +/-   ##
==========================================
+ Coverage   92.11%   92.43%   +0.31%     
==========================================
  Files          32       32              
  Lines        4259     5007     +748     
==========================================
+ Hits         3923     4628     +705     
- Misses        252      275      +23     
- Partials       84      104      +20     
Flag Coverage Δ
unittests 92.43% <94.38%> (+0.31%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rostislaved
Copy link

Really appreciate the work on this! Will a FilterErr variant come in a separate PR?

@samber
Copy link
Owner Author

samber commented Mar 1, 2026

Nope, it will be added with this PR.

10-15 more helpers are coming in a few minutes ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants