feat: adding FilterKeysErr + FilterValuesErr helpers

This commit is contained in:
Samuel Berthe
2026-03-01 20:22:31 +01:00
parent dd1d58e324
commit 72a33aa397
8 changed files with 332 additions and 0 deletions
+22
View File
@@ -2005,6 +2005,17 @@ result := FilterKeys(kv, func(k int, v string) bool {
// [1]
```
```go
// Use FilterKeysErr when the predicate can return an error
result, err := lo.FilterKeysErr(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) (bool, error) {
if k == 3 {
return false, fmt.Errorf("key 3 not allowed")
}
return v == "foo", nil
})
// []int(nil), error("key 3 not allowed")
```
[[play](https://go.dev/play/p/OFlKXlPrBAe)]
### FilterValues
@@ -2020,6 +2031,17 @@ result := FilterValues(kv, func(k int, v string) bool {
// ["foo"]
```
```go
// Use FilterValuesErr when the predicate can return an error
result, err := lo.FilterValuesErr(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) (bool, error) {
if k == 3 {
return false, fmt.Errorf("key 3 not allowed")
}
return v == "foo", nil
})
// []string(nil), error("key 3 not allowed")
```
[[play](https://go.dev/play/p/YVD5r_h-LX-)]
### Range / RangeFrom / RangeWithSteps
+1
View File
@@ -9,6 +9,7 @@ variantHelpers:
- core#map#filterkeys
similarHelpers:
- core#map#filtervalues
- core#map#filterkeyserr
- core#map#pickbykeys
- core#map#omitbykeys
- core#map#pickbyvalues
+38
View File
@@ -0,0 +1,38 @@
---
name: FilterKeysErr
slug: filterkeyserr
sourceRef: map.go#L498
category: core
subCategory: map
signatures:
- "func FilterKeysErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]K, error)"
playUrl:
variantHelpers:
- core#map#filterkeyserr
similarHelpers:
- core#map#filterkeys
- core#map#filtervalueserr
- core#slice#filter
position: 235
---
Transforms a map into a slice of keys based on a predicate that can return an error. It is a mix of Filter() and Keys() with error handling. If the predicate returns true, the key is included. If the predicate returns an error, iteration stops immediately and returns the error.
```go
kv := map[int]string{1:"foo", 2:"bar", 3:"baz"}
result, err := lo.FilterKeysErr(kv, func(k int, v string) (bool, error) {
if k == 3 {
return false, errors.New("key 3 not allowed")
}
return v == "foo", nil
})
// []int(nil), error("key 3 not allowed")
```
```go
kv := map[int]string{1:"foo", 2:"bar", 3:"baz"}
result, err := lo.FilterKeysErr(kv, func(k int, v string) (bool, error) {
return v == "bar", nil
})
// []int{2}, nil
```
+1
View File
@@ -9,6 +9,7 @@ variantHelpers:
- core#map#filtervalues
similarHelpers:
- core#map#filterkeys
- core#map#filtervalueserr
- core#map#pickbyvalues
- core#map#omitbyvalues
- core#map#pickbykeys
+38
View File
@@ -0,0 +1,38 @@
---
name: FilterValuesErr
slug: filtervalueserr
sourceRef: map.go#L519
category: core
subCategory: map
signatures:
- "func FilterValuesErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]V, error)"
playUrl:
variantHelpers:
- core#map#filtervalueserr
similarHelpers:
- core#map#filtervalues
- core#map#filterkeyserr
- core#slice#filter
position: 245
---
Transforms a map into a slice of values based on a predicate that can return an error. It is a mix of Filter() and Values() with error handling. If the predicate returns true, the value is included. If the predicate returns an error, iteration stops immediately and returns the error.
```go
kv := map[int]string{1:"foo", 2:"bar", 3:"baz"}
result, err := lo.FilterValuesErr(kv, func(k int, v string) (bool, error) {
if k == 3 {
return false, errors.New("key 3 not allowed")
}
return v == "foo", nil
})
// []string(nil), error("key 3 not allowed")
```
```go
kv := map[int]string{1:"foo", 2:"bar", 3:"baz"}
result, err := lo.FilterValuesErr(kv, func(k int, v string) (bool, error) {
return v == "bar", nil
})
// []string{"bar"}, nil
```
+40
View File
@@ -2167,6 +2167,46 @@ func ExampleFilterValues() {
// Output: [foo]
}
func ExampleFilterKeysErr() {
kv := map[int]string{1: "foo", 2: "bar", 3: "baz"}
result, err := FilterKeysErr(kv, func(k int, v string) (bool, error) {
if k == 3 {
return false, fmt.Errorf("key 3 not allowed")
}
return v == "foo", nil
})
fmt.Printf("%v, %v\n", result, err)
result, err = FilterKeysErr(kv, func(k int, v string) (bool, error) {
return v == "bar", nil
})
fmt.Printf("%v, %v\n", result, err)
// Output:
// [], key 3 not allowed
// [2], <nil>
}
func ExampleFilterValuesErr() {
kv := map[int]string{1: "foo", 2: "bar", 3: "baz"}
result, err := FilterValuesErr(kv, func(k int, v string) (bool, error) {
if k == 3 {
return false, fmt.Errorf("key 3 not allowed")
}
return v == "foo", nil
})
fmt.Printf("%v, %v\n", result, err)
result, err = FilterValuesErr(kv, func(k int, v string) (bool, error) {
return v == "bar", nil
})
fmt.Printf("%v, %v\n", result, err)
// Output:
// [], key 3 not allowed
// [bar], <nil>
}
func ExampleRange() {
result1 := Range(4)
result2 := Range(-4)
+42
View File
@@ -489,3 +489,45 @@ func FilterValues[K comparable, V any](in map[K]V, predicate func(key K, value V
return result
}
// FilterKeysErr transforms a map into a slice of keys based on predicate that can return an error.
// It is a mix of lo.Filter() and lo.Keys() with error handling.
// If the predicate returns true, the key is added to the result slice.
// If the predicate returns an error, iteration stops immediately and returns the error.
// The order of the keys in the input map is not specified.
func FilterKeysErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]K, error) {
result := make([]K, 0)
for k, v := range in {
ok, err := predicate(k, v)
if err != nil {
return nil, err
}
if ok {
result = append(result, k)
}
}
return result, nil
}
// FilterValuesErr transforms a map into a slice of values based on predicate that can return an error.
// It is a mix of lo.Filter() and lo.Values() with error handling.
// If the predicate returns true, the value is added to the result slice.
// If the predicate returns an error, iteration stops immediately and returns the error.
// The order of the keys in the input map is not specified.
func FilterValuesErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]V, error) {
result := make([]V, 0)
for k, v := range in {
ok, err := predicate(k, v)
if err != nil {
return nil, err
}
if ok {
result = append(result, v)
}
}
return result, nil
}
+150
View File
@@ -1234,6 +1234,156 @@ func TestFilterValues(t *testing.T) {
is.Empty(result2)
}
func TestFilterKeysErr(t *testing.T) {
t.Parallel()
is := assert.New(t)
tests := []struct {
name string
input map[int]string
predicate func(int, string) (bool, error)
want []int
wantErr string
}{
{
name: "filter by value",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
return v == "foo", nil
},
want: []int{1},
},
{
name: "empty map",
input: map[int]string{},
predicate: func(k int, v string) (bool, error) {
return true, nil
},
want: []int{},
},
{
name: "filter all out",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
return false, nil
},
want: []int{},
},
{
name: "filter all in",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
return true, nil
},
want: []int{1, 2, 3},
},
{
name: "error on specific key",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
if k == 2 {
return false, fmt.Errorf("key 2 not allowed")
}
return true, nil
},
wantErr: "key 2 not allowed",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := FilterKeysErr(tt.input, tt.predicate)
if tt.wantErr != "" {
is.Error(err)
is.Equal(tt.wantErr, err.Error())
is.Nil(got)
} else {
is.NoError(err)
is.ElementsMatch(tt.want, got)
}
})
}
}
func TestFilterValuesErr(t *testing.T) {
t.Parallel()
is := assert.New(t)
tests := []struct {
name string
input map[int]string
predicate func(int, string) (bool, error)
want []string
wantErr string
}{
{
name: "filter by value",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
return v == "foo", nil
},
want: []string{"foo"},
},
{
name: "empty map",
input: map[int]string{},
predicate: func(k int, v string) (bool, error) {
return true, nil
},
want: []string{},
},
{
name: "filter all out",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
return false, nil
},
want: []string{},
},
{
name: "filter all in",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
return true, nil
},
want: []string{"foo", "bar", "baz"},
},
{
name: "error on specific key",
input: map[int]string{1: "foo", 2: "bar", 3: "baz"},
predicate: func(k int, v string) (bool, error) {
if k == 2 {
return false, fmt.Errorf("key 2 not allowed")
}
return true, nil
},
wantErr: "key 2 not allowed",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := FilterValuesErr(tt.input, tt.predicate)
if tt.wantErr != "" {
is.Error(err)
is.Equal(tt.wantErr, err.Error())
is.Nil(got)
} else {
is.NoError(err)
is.ElementsMatch(tt.want, got)
}
})
}
}
func BenchmarkAssign(b *testing.B) {
counts := []int{32768, 1024, 128, 32, 2}