From e03ba5d1b3d581c6885274c2cd34ead6c0e586fb Mon Sep 17 00:00:00 2001 From: Ruidy Date: Fri, 14 Nov 2025 14:52:28 +0100 Subject: [PATCH] feat: add Sliding window function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Sliding: creates sliding window views of a slice - Pre-allocated for optimal performance - Returns independent window copies (non-mutating) - Comprehensive tests including edge cases - Benchmark included Example: Sliding([1,2,3,4,5], 3) → [[1,2,3], [2,3,4], [3,4,5]] Resolves Issue 19 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- sliding.go | 23 +++++++++++++ sliding_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 sliding.go create mode 100644 sliding_test.go diff --git a/sliding.go b/sliding.go new file mode 100644 index 0000000..491ec66 --- /dev/null +++ b/sliding.go @@ -0,0 +1,23 @@ +package underscore + +// Sliding creates a sliding window view of the slice with the specified window size. +// Returns an empty slice if size is less than or equal to 0. +// Returns an empty slice if size is greater than the slice length. +// +// Example: Sliding([]int{1,2,3,4,5}, 3) → [[1,2,3], [2,3,4], [3,4,5]] +func Sliding[T any](values []T, size int) [][]T { + if size <= 0 || size > len(values) { + return [][]T{} + } + + windowCount := len(values) - size + 1 + res := make([][]T, 0, windowCount) + + for i := 0; i <= len(values)-size; i++ { + window := make([]T, size) + copy(window, values[i:i+size]) + res = append(res, window) + } + + return res +} diff --git a/sliding_test.go b/sliding_test.go new file mode 100644 index 0000000..9a9f38c --- /dev/null +++ b/sliding_test.go @@ -0,0 +1,90 @@ +package underscore_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + u "github.com/rjNemo/underscore" +) + +func TestSliding(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + result := u.Sliding(nums, 3) + expected := [][]int{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}} + assert.Equal(t, expected, result) +} + +func TestSlidingEmpty(t *testing.T) { + result := u.Sliding([]int{}, 3) + assert.Equal(t, [][]int{}, result) +} + +func TestSlidingSizeOne(t *testing.T) { + nums := []int{1, 2, 3} + result := u.Sliding(nums, 1) + expected := [][]int{{1}, {2}, {3}} + assert.Equal(t, expected, result) +} + +func TestSlidingSizeEqualLength(t *testing.T) { + nums := []int{1, 2, 3} + result := u.Sliding(nums, 3) + expected := [][]int{{1, 2, 3}} + assert.Equal(t, expected, result) +} + +func TestSlidingSizeGreaterThanLength(t *testing.T) { + nums := []int{1, 2, 3} + result := u.Sliding(nums, 5) + assert.Equal(t, [][]int{}, result) +} + +func TestSlidingSizeZero(t *testing.T) { + nums := []int{1, 2, 3} + result := u.Sliding(nums, 0) + assert.Equal(t, [][]int{}, result) +} + +func TestSlidingSizeNegative(t *testing.T) { + nums := []int{1, 2, 3} + result := u.Sliding(nums, -1) + assert.Equal(t, [][]int{}, result) +} + +func TestSlidingTwoElements(t *testing.T) { + nums := []int{1, 2, 3, 4} + result := u.Sliding(nums, 2) + expected := [][]int{{1, 2}, {2, 3}, {3, 4}} + assert.Equal(t, expected, result) +} + +func TestSlidingStrings(t *testing.T) { + words := []string{"a", "b", "c", "d"} + result := u.Sliding(words, 2) + expected := [][]string{{"a", "b"}, {"b", "c"}, {"c", "d"}} + assert.Equal(t, expected, result) +} + +func TestSlidingDoesNotMutate(t *testing.T) { + original := []int{1, 2, 3, 4} + result := u.Sliding(original, 2) + + // Modify a window + result[0][0] = 999 + + // Original should be unchanged + assert.Equal(t, 1, original[0]) +} + +func BenchmarkSliding(b *testing.B) { + nums := make([]int, 100) + for i := range nums { + nums[i] = i + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + u.Sliding(nums, 10) + } +}