mirror of
https://github.com/rjNemo/underscore
synced 2026-06-06 02:26:42 +00:00
feat: add Scan function (reduce with history) (#43)
- 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
b35a87e50c
commit
0bf04c224e
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