mirror of
https://github.com/samber/lo.git
synced 2026-04-22 15:37:14 +08:00
feat: adding FilterErr helpers
This commit is contained in:
@@ -393,6 +393,17 @@ even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
|
||||
// []int{2, 4}
|
||||
```
|
||||
|
||||
```go
|
||||
// Use FilterErr when the predicate can return an error
|
||||
even, err := lo.FilterErr([]int{1, 2, 3, 4}, func(x int, _ int) (bool, error) {
|
||||
if x == 3 {
|
||||
return false, fmt.Errorf("number 3 is not allowed")
|
||||
}
|
||||
return x%2 == 0, nil
|
||||
})
|
||||
// []int(nil), error("number 3 is not allowed")
|
||||
```
|
||||
|
||||
[[play](https://go.dev/play/p/Apjg3WeSi7K)]
|
||||
|
||||
Mutable: like `lo.Filter()`, but the slice is updated in place.
|
||||
|
||||
@@ -7,6 +7,7 @@ subCategory: slice
|
||||
playUrl: https://go.dev/play/p/Apjg3WeSi7K
|
||||
similarHelpers:
|
||||
- core#slice#reject
|
||||
- core#slice#filtererr
|
||||
- core#slice#filtermap
|
||||
- core#slice#filterreject
|
||||
- core#slice#rejectmap
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: FilterErr
|
||||
slug: filtererr
|
||||
sourceRef: slice.go#L27
|
||||
category: core
|
||||
subCategory: slice
|
||||
signatures:
|
||||
- "func FilterErr[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) (bool, error)) (Slice, error)"
|
||||
playUrl:
|
||||
variantHelpers:
|
||||
- core#slice#filtererr
|
||||
similarHelpers:
|
||||
- core#slice#filter
|
||||
- core#slice#reject
|
||||
- core#slice#filtermap
|
||||
- core#slice#filterreject
|
||||
position: 5
|
||||
---
|
||||
|
||||
Iterates over a collection and returns a slice of all the elements the predicate function returns `true` for. If the predicate returns an error, iteration stops immediately and returns the error.
|
||||
|
||||
```go
|
||||
even, err := lo.FilterErr([]int{1, 2, 3, 4}, func(x int, index int) (bool, error) {
|
||||
if x == 3 {
|
||||
return false, errors.New("number 3 is not allowed")
|
||||
}
|
||||
return x%2 == 0, nil
|
||||
})
|
||||
// []int(nil), error("number 3 is not allowed")
|
||||
```
|
||||
|
||||
```go
|
||||
even, err := lo.FilterErr([]int{1, 2, 3, 4}, func(x int, index int) (bool, error) {
|
||||
return x%2 == 0, nil
|
||||
})
|
||||
// []int{2, 4}, nil
|
||||
```
|
||||
@@ -2354,6 +2354,26 @@ func ExampleFilter() {
|
||||
// Output: [2 4]
|
||||
}
|
||||
|
||||
func ExampleFilterErr() {
|
||||
list := []int64{1, 2, 3, 4}
|
||||
|
||||
result, err := FilterErr(list, func(nbr int64, index int) (bool, error) {
|
||||
if nbr == 3 {
|
||||
return false, fmt.Errorf("number 3 is not allowed")
|
||||
}
|
||||
return nbr%2 == 0, nil
|
||||
})
|
||||
fmt.Printf("%v, %v\n", result, err)
|
||||
|
||||
result, err = FilterErr([]int64{1, 2, 4, 6}, func(nbr int64, index int) (bool, error) {
|
||||
return nbr%2 == 0, nil
|
||||
})
|
||||
fmt.Printf("%v, %v\n", result, err)
|
||||
// Output:
|
||||
// [], number 3 is not allowed
|
||||
// [2 4 6], <nil>
|
||||
}
|
||||
|
||||
func ExampleMap() {
|
||||
list := []int64{1, 2, 3, 4}
|
||||
|
||||
|
||||
@@ -21,6 +21,25 @@ func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T, index in
|
||||
return result
|
||||
}
|
||||
|
||||
// FilterErr iterates over elements of collection, returning a slice of all elements predicate returns true for.
|
||||
// If the predicate returns an error, iteration stops immediately and returns the error.
|
||||
// Play: https://go.dev/play/p/Apjg3WeSi7K
|
||||
func FilterErr[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) (bool, error)) (Slice, error) {
|
||||
result := make(Slice, 0, len(collection))
|
||||
|
||||
for i := range collection {
|
||||
ok, err := predicate(collection[i], i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
result = append(result, collection[i])
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Map manipulates a slice and transforms it to a slice of another type.
|
||||
// Play: https://go.dev/play/p/OkPcYAhBo0D
|
||||
func Map[T, R any](collection []T, transform func(item T, index int) R) []R {
|
||||
|
||||
@@ -34,6 +34,105 @@ func TestFilter(t *testing.T) {
|
||||
is.IsType(nonempty, allStrings, "type preserved")
|
||||
}
|
||||
|
||||
func TestFilterErr(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := assert.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input []int
|
||||
predicate func(item int, index int) (bool, error)
|
||||
want []int
|
||||
wantErr string
|
||||
callbacks int // Number of predicates called before error/finish
|
||||
}{
|
||||
{
|
||||
name: "filter even numbers",
|
||||
input: []int{1, 2, 3, 4},
|
||||
predicate: func(x int, _ int) (bool, error) {
|
||||
return x%2 == 0, nil
|
||||
},
|
||||
want: []int{2, 4},
|
||||
callbacks: 4,
|
||||
},
|
||||
{
|
||||
name: "empty slice",
|
||||
input: []int{},
|
||||
predicate: func(x int, _ int) (bool, error) {
|
||||
return true, nil
|
||||
},
|
||||
want: []int{},
|
||||
callbacks: 0,
|
||||
},
|
||||
{
|
||||
name: "filter all out",
|
||||
input: []int{1, 2, 3, 4},
|
||||
predicate: func(x int, _ int) (bool, error) {
|
||||
return false, nil
|
||||
},
|
||||
want: []int{},
|
||||
callbacks: 4,
|
||||
},
|
||||
{
|
||||
name: "filter all in",
|
||||
input: []int{1, 2, 3, 4},
|
||||
predicate: func(x int, _ int) (bool, error) {
|
||||
return true, nil
|
||||
},
|
||||
want: []int{1, 2, 3, 4},
|
||||
callbacks: 4,
|
||||
},
|
||||
{
|
||||
name: "error on specific index",
|
||||
input: []int{1, 2, 3, 4},
|
||||
predicate: func(x int, _ int) (bool, error) {
|
||||
if x == 3 {
|
||||
return false, fmt.Errorf("number 3 is not allowed")
|
||||
}
|
||||
return x%2 == 0, nil
|
||||
},
|
||||
callbacks: 3,
|
||||
wantErr: "number 3 is not allowed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var callbacks int
|
||||
wrappedPredicate := func(item int, index int) (bool, error) {
|
||||
callbacks++
|
||||
return tt.predicate(item, index)
|
||||
}
|
||||
|
||||
got, err := FilterErr(tt.input, wrappedPredicate)
|
||||
|
||||
if tt.wantErr != "" {
|
||||
is.Error(err)
|
||||
is.Equal(tt.wantErr, err.Error())
|
||||
is.Nil(got)
|
||||
is.Equal(tt.callbacks, callbacks, "callback count should match expected early return")
|
||||
} else {
|
||||
is.NoError(err)
|
||||
is.Equal(tt.want, got)
|
||||
is.Equal(tt.callbacks, callbacks)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Test type preservation
|
||||
type myStrings []string
|
||||
allStrings := myStrings{"", "foo", "bar"}
|
||||
nonempty, err := FilterErr(allStrings, func(x string, _ int) (bool, error) {
|
||||
return len(x) > 0, nil
|
||||
})
|
||||
is.NoError(err)
|
||||
is.IsType(nonempty, allStrings, "type preserved")
|
||||
is.Equal(myStrings{"foo", "bar"}, nonempty)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := assert.New(t)
|
||||
|
||||
Reference in New Issue
Block a user