diff --git a/moslice/find.go b/moslice/find.go index 345dbd4..f0bfaf0 100644 --- a/moslice/find.go +++ b/moslice/find.go @@ -20,6 +20,16 @@ func FirstWhere[T any](ts []T, predicate func(t T) bool) T { return zeroT } +// FirstIndexWhere returns the index of the first element in the slice that satisfies the predicate. An empty slice will return -1 +func FirstIndexWhere[T any](ts []T, predicate func(t T) bool) int { + for i, t := range ts { + if predicate(t) { + return i + } + } + return -1 +} + func FindWhere[T any](ts []T, predicate func(t T) bool) (T, bool) { var zeroT T @@ -41,3 +51,26 @@ func FindWithIndexWhere[T any](ts []T, predicate func(t T) bool) (T, int, bool) } return zeroT, 0, false } + +// AnyWhere returns true if any element in the slice satisfies the predicate. An empty slice will return false +func AnyWhere[T any](ts []T, predicate func(t T) bool) bool { + for _, t := range ts { + if predicate(t) { + return true + } + } + return false +} + +// AllWhere returns true if all the element in the slice satisfies the predicate. An empty slice will return false +func AllWhere[T any](ts []T, predicate func(t T) bool) bool { + if len(ts) == 0 { + return false + } + for _, t := range ts { + if !predicate(t) { + return false + } + } + return true +} diff --git a/moslice/find_test.go b/moslice/find_test.go index 8bd76b5..c934c39 100644 --- a/moslice/find_test.go +++ b/moslice/find_test.go @@ -1,9 +1,11 @@ package moslice_test import ( - "lmika.dev/pkg/modash/moslice" + "strings" "testing" + "lmika.dev/pkg/modash/moslice" + "github.com/stretchr/testify/assert" ) @@ -23,3 +25,117 @@ func TestContains(t *testing.T) { assert.False(t, moslice.Contains(strs, "bla")) }) } + +func TestFirstWhere(t *testing.T) { + var ( + ints = []int{1, 2, 3, 4, 5} + strs = []string{"foo", "bar", "baz"} + ) + + t.Run("should return the first element that satisfies the predicate", func(t *testing.T) { + assert.Equal(t, 2, moslice.FirstWhere(ints, func(x int) bool { return x%2 == 0 })) + assert.Equal(t, "bar", moslice.FirstWhere(strs, func(x string) bool { return strings.HasPrefix(x, "b") })) + }) + + t.Run("should return the element if the first element satisfies the predicate", func(t *testing.T) { + assert.Equal(t, 1, moslice.FirstWhere(ints, func(x int) bool { return x%2 == 1 })) + assert.Equal(t, "foo", moslice.FirstWhere(strs, func(x string) bool { return strings.HasPrefix(x, "f") })) + }) + + t.Run("should return the zero value if no element satisfies the predicate", func(t *testing.T) { + assert.Equal(t, 0, moslice.FirstWhere(ints, func(x int) bool { return x > 10 })) + assert.Equal(t, "", moslice.FirstWhere(strs, func(x string) bool { return strings.HasPrefix(x, "z") })) + }) + + t.Run("should return the zero value if the passed in slice is empty", func(t *testing.T) { + assert.Equal(t, 0, moslice.FirstWhere([]int{}, func(x int) bool { return true })) + }) + + t.Run("should return the zero value if the passed in slice is nil", func(t *testing.T) { + assert.Equal(t, 0, moslice.FirstWhere(nil, func(x int) bool { return true })) + }) +} + +func TestFirstIndexWhere(t *testing.T) { + var ( + ints = []int{1, 2, 3, 4, 5} + strs = []string{"foo", "bar", "baz"} + ) + + t.Run("should return the index of the first element that satisfies the predicate", func(t *testing.T) { + assert.Equal(t, 1, moslice.FirstIndexWhere(ints, func(x int) bool { return x%2 == 0 })) + assert.Equal(t, 1, moslice.FirstIndexWhere(strs, func(x string) bool { return strings.HasPrefix(x, "b") })) + }) + + t.Run("should return 0 if the first element satisfies the predicate", func(t *testing.T) { + assert.Equal(t, 0, moslice.FirstIndexWhere(ints, func(x int) bool { return x%2 == 1 })) + assert.Equal(t, 0, moslice.FirstIndexWhere(strs, func(x string) bool { return strings.HasPrefix(x, "f") })) + }) + + t.Run("should return -1 if no element satisfies the predicate", func(t *testing.T) { + assert.Equal(t, -1, moslice.FirstIndexWhere(ints, func(x int) bool { return x > 10 })) + assert.Equal(t, -1, moslice.FirstIndexWhere(strs, func(x string) bool { return strings.HasPrefix(x, "z") })) + }) + + t.Run("should return -1 if the passed in slice is empty", func(t *testing.T) { + assert.Equal(t, -1, moslice.FirstIndexWhere([]int{}, func(x int) bool { return true })) + }) + + t.Run("should return -1 if the passed in slice is nil", func(t *testing.T) { + assert.Equal(t, -1, moslice.FirstIndexWhere(nil, func(x int) bool { return true })) + }) +} + +func TestAnyWhere(t *testing.T) { + var ( + ints = []int{1, 2, 3, 4, 5} + strs = []string{"foo", "bar", "baz"} + ) + + t.Run("should return true if any element satisfies the predicate", func(t *testing.T) { + assert.True(t, moslice.AnyWhere(ints, func(x int) bool { return x%2 == 0 })) + assert.True(t, moslice.AnyWhere(strs, func(x string) bool { return strings.HasPrefix(x, "ba") })) + }) + + t.Run("should return false if no element satisfies the predicate", func(t *testing.T) { + assert.False(t, moslice.AnyWhere(ints, func(x int) bool { return x > 10 })) + assert.False(t, moslice.AnyWhere(strs, func(x string) bool { return strings.HasPrefix(x, "z") })) + }) + + t.Run("should return false if the passed in slice is empty", func(t *testing.T) { + assert.False(t, moslice.AnyWhere([]int{}, func(x int) bool { return true })) + }) + + t.Run("should return false if the passed in slice is nil", func(t *testing.T) { + assert.False(t, moslice.AnyWhere(nil, func(x int) bool { return true })) + }) +} + +func TestAllWhere(t *testing.T) { + var ( + ints = []int{2, 4, 6} + strs = []string{"foo", "bar", "baz"} + ) + + t.Run("should return true if all elements satisfy the predicate", func(t *testing.T) { + assert.True(t, moslice.AllWhere(ints, func(x int) bool { return x%2 == 0 })) + assert.True(t, moslice.AllWhere(strs, func(x string) bool { return len(x) == 3 })) + }) + + t.Run("should return false if some elements do not satisfy the predicate", func(t *testing.T) { + assert.False(t, moslice.AllWhere(ints, func(x int) bool { return x > 3 })) + assert.False(t, moslice.AllWhere(strs, func(x string) bool { return strings.HasPrefix(x, "b") })) + }) + + t.Run("should return false if no elements satisfy the predicate", func(t *testing.T) { + assert.False(t, moslice.AllWhere(ints, func(x int) bool { return x > 10 })) + }) + + t.Run("should return false if the passed in slice is empty", func(t *testing.T) { + assert.False(t, moslice.AllWhere([]int{}, func(x int) bool { return true })) + }) + + t.Run("should return false if the passed in slice is nil", func(t *testing.T) { + assert.False(t, moslice.AllWhere(nil, func(x int) bool { return true })) + }) +}