mirror of
https://github.com/rjNemo/underscore
synced 2026-06-06 02:26:42 +00:00
feat: add Scan function (reduce with history)
- Add Scan: returns all intermediate accumulator values
- Also known as prefix scan or cumulative fold
- Comprehensive tests including edge cases and different types
- Benchmark included
Example: Scan([]int{1,2,3,4}, 0, +) → [1, 3, 6, 10]
Resolves Issue 15
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d622c8cba8
commit
5c8e892c40
2 changed files with 86 additions and 0 deletions
18
scan.go
Normal file
18
scan.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package underscore
|
||||
|
||||
// Scan is like Reduce but returns all intermediate accumulator values.
|
||||
// Also known as prefix scan or cumulative fold.
|
||||
//
|
||||
// Example: Scan([]int{1,2,3,4}, 0, func(acc, n int) int { return acc + n }) → [1, 3, 6, 10]
|
||||
func Scan[T, P any](values []T, acc P, fn func(P, T) P) []P {
|
||||
if len(values) == 0 {
|
||||
return []P{}
|
||||
}
|
||||
|
||||
res := make([]P, 0, len(values))
|
||||
for _, v := range values {
|
||||
acc = fn(acc, v)
|
||||
res = append(res, acc)
|
||||
}
|
||||
return res
|
||||
}
|
||||
68
scan_test.go
Normal file
68
scan_test.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package underscore_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 4}
|
||||
result := u.Scan(nums, 0, func(acc, n int) int { return acc + n })
|
||||
assert.Equal(t, []int{1, 3, 6, 10}, result)
|
||||
}
|
||||
|
||||
func TestScanEmpty(t *testing.T) {
|
||||
result := u.Scan([]int{}, 0, func(acc, n int) int { return acc + n })
|
||||
assert.Equal(t, []int{}, result)
|
||||
}
|
||||
|
||||
func TestScanSingleElement(t *testing.T) {
|
||||
result := u.Scan([]int{5}, 0, func(acc, n int) int { return acc + n })
|
||||
assert.Equal(t, []int{5}, result)
|
||||
}
|
||||
|
||||
func TestScanMultiplication(t *testing.T) {
|
||||
nums := []int{2, 3, 4}
|
||||
result := u.Scan(nums, 1, func(acc, n int) int { return acc * n })
|
||||
assert.Equal(t, []int{2, 6, 24}, result)
|
||||
}
|
||||
|
||||
func TestScanStrings(t *testing.T) {
|
||||
words := []string{"hello", "world", "!"}
|
||||
result := u.Scan(words, "", func(acc, s string) string { return acc + s })
|
||||
assert.Equal(t, []string{"hello", "helloworld", "helloworld!"}, result)
|
||||
}
|
||||
|
||||
func TestScanMax(t *testing.T) {
|
||||
nums := []int{3, 1, 4, 1, 5, 9, 2}
|
||||
result := u.Scan(nums, 0, func(acc, n int) int {
|
||||
if n > acc {
|
||||
return n
|
||||
}
|
||||
return acc
|
||||
})
|
||||
assert.Equal(t, []int{3, 3, 4, 4, 5, 9, 9}, result)
|
||||
}
|
||||
|
||||
func TestScanDifferentTypes(t *testing.T) {
|
||||
nums := []int{1, 2, 3}
|
||||
result := u.Scan(nums, 0.0, func(acc float64, n int) float64 {
|
||||
return acc + float64(n)*2.5
|
||||
})
|
||||
assert.Equal(t, []float64{2.5, 7.5, 15.0}, result)
|
||||
}
|
||||
|
||||
func BenchmarkScan(b *testing.B) {
|
||||
nums := make([]int, 1000)
|
||||
for i := range nums {
|
||||
nums[i] = i
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
u.Scan(nums, 0, func(acc, n int) int { return acc + n })
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue