From 482b8991dfe664fd1b73a501258047e9ecc3c007 Mon Sep 17 00:00:00 2001 From: Ruidy Date: Fri, 14 Nov 2025 14:47:53 +0100 Subject: [PATCH] feat: add TakeWhile and DropWhile functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add TakeWhile: returns elements while predicate is true - Add DropWhile: drops elements while predicate is true - Comprehensive tests including edge cases - Benchmarks included Resolves Issue 14 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- dropwhile.go | 15 +++++++++++++ dropwhile_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++ takewhile.go | 17 +++++++++++++++ takewhile_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 dropwhile.go create mode 100644 dropwhile_test.go create mode 100644 takewhile.go create mode 100644 takewhile_test.go diff --git a/dropwhile.go b/dropwhile.go new file mode 100644 index 0000000..939f097 --- /dev/null +++ b/dropwhile.go @@ -0,0 +1,15 @@ +package underscore + +// DropWhile drops elements from the beginning of the slice while the predicate returns true. +// It returns the remaining elements starting from the first element where the predicate returns false. +func DropWhile[T any](values []T, predicate func(T) bool) []T { + for i, v := range values { + if !predicate(v) { + res := make([]T, len(values)-i) + copy(res, values[i:]) + return res + } + } + // All elements satisfy predicate, return empty slice + return []T{} +} diff --git a/dropwhile_test.go b/dropwhile_test.go new file mode 100644 index 0000000..4703bde --- /dev/null +++ b/dropwhile_test.go @@ -0,0 +1,55 @@ +package underscore_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + u "github.com/rjNemo/underscore" +) + +func TestDropWhile(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + result := u.DropWhile(nums, func(n int) bool { return n < 5 }) + assert.Equal(t, []int{5, 6, 7, 8, 9}, result) +} + +func TestDropWhileEmpty(t *testing.T) { + result := u.DropWhile([]int{}, func(n int) bool { return n < 5 }) + assert.Equal(t, []int{}, result) +} + +func TestDropWhileNoneMatch(t *testing.T) { + nums := []int{5, 6, 7, 8, 9} + result := u.DropWhile(nums, func(n int) bool { return n < 5 }) + assert.Equal(t, []int{5, 6, 7, 8, 9}, result) +} + +func TestDropWhileAllMatch(t *testing.T) { + nums := []int{1, 2, 3, 4} + result := u.DropWhile(nums, func(n int) bool { return n < 10 }) + assert.Equal(t, []int{}, result) +} + +func TestDropWhileSingleElement(t *testing.T) { + result := u.DropWhile([]int{5}, func(n int) bool { return n < 10 }) + assert.Equal(t, []int{}, result) +} + +func TestDropWhileStrings(t *testing.T) { + words := []string{"apple", "banana", "cherry", "date"} + result := u.DropWhile(words, func(s string) bool { return len(s) < 6 }) + assert.Equal(t, []string{"banana", "cherry", "date"}, result) +} + +func BenchmarkDropWhile(b *testing.B) { + nums := make([]int, 1000) + for i := range nums { + nums[i] = i + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + u.DropWhile(nums, func(n int) bool { return n < 500 }) + } +} diff --git a/takewhile.go b/takewhile.go new file mode 100644 index 0000000..376b829 --- /dev/null +++ b/takewhile.go @@ -0,0 +1,17 @@ +package underscore + +// TakeWhile returns elements from the beginning of the slice while the predicate returns true. +// It stops at the first element where the predicate returns false. +func TakeWhile[T any](values []T, predicate func(T) bool) []T { + for i, v := range values { + if !predicate(v) { + res := make([]T, i) + copy(res, values[:i]) + return res + } + } + // All elements satisfy predicate + res := make([]T, len(values)) + copy(res, values) + return res +} diff --git a/takewhile_test.go b/takewhile_test.go new file mode 100644 index 0000000..f2a449f --- /dev/null +++ b/takewhile_test.go @@ -0,0 +1,55 @@ +package underscore_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + u "github.com/rjNemo/underscore" +) + +func TestTakeWhile(t *testing.T) { + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + result := u.TakeWhile(nums, func(n int) bool { return n < 5 }) + assert.Equal(t, []int{1, 2, 3, 4}, result) +} + +func TestTakeWhileEmpty(t *testing.T) { + result := u.TakeWhile([]int{}, func(n int) bool { return n < 5 }) + assert.Equal(t, []int{}, result) +} + +func TestTakeWhileNoneMatch(t *testing.T) { + nums := []int{5, 6, 7, 8, 9} + result := u.TakeWhile(nums, func(n int) bool { return n < 5 }) + assert.Equal(t, []int{}, result) +} + +func TestTakeWhileAllMatch(t *testing.T) { + nums := []int{1, 2, 3, 4} + result := u.TakeWhile(nums, func(n int) bool { return n < 10 }) + assert.Equal(t, []int{1, 2, 3, 4}, result) +} + +func TestTakeWhileSingleElement(t *testing.T) { + result := u.TakeWhile([]int{5}, func(n int) bool { return n < 10 }) + assert.Equal(t, []int{5}, result) +} + +func TestTakeWhileStrings(t *testing.T) { + words := []string{"apple", "banana", "cherry", "date"} + result := u.TakeWhile(words, func(s string) bool { return len(s) < 6 }) + assert.Equal(t, []string{"apple"}, result) +} + +func BenchmarkTakeWhile(b *testing.B) { + nums := make([]int, 1000) + for i := range nums { + nums[i] = i + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + u.TakeWhile(nums, func(n int) bool { return n < 500 }) + } +}