mirror of
https://github.com/rjNemo/underscore
synced 2026-06-06 02:26:42 +00:00
docs: add documentation for new collection functions
Add comprehensive documentation for all new functions: - TakeWhile: take elements while predicate is true - DropWhile: drop elements while predicate is true - Scan: running accumulator (prefix scan) - First/FirstN: get first element(s) safely - Init: all but last element - Intersperse: insert separator between elements - Sliding: sliding window views - FoldRight: right-to-left fold/reduce - Tap: side effects without mutation - Transpose: flip matrix rows/columns - Unzip: split tuples into separate slices - ParallelReduce: parallel reduction (experimental) - Replicate: create n copies of a value Each doc includes: - Clear description - Code examples with output - Common use cases - Edge case handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2651a3331a
commit
bcb4dd1e9d
15 changed files with 525 additions and 0 deletions
13
.claude/settings.local.json
Normal file
13
.claude/settings.local.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(TestDropWhile\"\ngo test -bench=\"BenchmarkTakeWhile)",
|
||||
"Bash(gh pr create:*)",
|
||||
"Bash(TestTranspose)",
|
||||
"Bash(TestUnzip)",
|
||||
"Bash(TestParallelReduce)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
25
docs/content/collections/dropwhile.md
Normal file
25
docs/content/collections/dropwhile.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
title: "DropWhile"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`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.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
lessThan5 := func(n int) bool { return n < 5 }
|
||||
fmt.Println(u.DropWhile(nums, lessThan5)) // [5, 6, 7, 8, 9]
|
||||
|
||||
words := []string{"apple", "banana", "cherry", "date"}
|
||||
shortWords := func(s string) bool { return len(s) < 6 }
|
||||
fmt.Println(u.DropWhile(words, shortWords)) // ["banana", "cherry", "date"]
|
||||
}
|
||||
```
|
||||
31
docs/content/collections/first.md
Normal file
31
docs/content/collections/first.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
title: "First"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`First` returns the first element of the slice. Returns an error if the slice is empty.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
first, err := u.First(nums)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(first) // 1
|
||||
|
||||
// Handle empty slice
|
||||
empty := []int{}
|
||||
_, err = u.First(empty)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err) // Error: underscore: empty slice
|
||||
}
|
||||
}
|
||||
```
|
||||
23
docs/content/collections/firstn.md
Normal file
23
docs/content/collections/firstn.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: "FirstN"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`FirstN` returns the first n elements of the slice. If n is greater than the slice length, returns the entire slice. If n is less than or equal to 0, returns an empty slice.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
fmt.Println(u.FirstN(nums, 3)) // [1, 2, 3]
|
||||
fmt.Println(u.FirstN(nums, 0)) // []
|
||||
fmt.Println(u.FirstN(nums, 10)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
fmt.Println(u.FirstN(nums, -5)) // []
|
||||
}
|
||||
```
|
||||
39
docs/content/collections/foldright.md
Normal file
39
docs/content/collections/foldright.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
title: "FoldRight"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`FoldRight` is like Reduce but processes elements from right to left. Also known as foldr in Haskell. Important for non-associative operations where the order of evaluation matters.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Subtraction is non-associative
|
||||
nums := []int{1, 2, 3}
|
||||
|
||||
// FoldRight: 1 - (2 - (3 - 0)) = 1 - (2 - 3) = 1 - (-1) = 2
|
||||
result := u.FoldRight(nums, 0, func(n, acc int) int { return n - acc })
|
||||
fmt.Println(result) // 2
|
||||
|
||||
// Compare with Reduce (left fold): (0 - 1) - 2 - 3 = -6
|
||||
leftResult := u.Reduce(nums, func(n, acc int) int { return acc - n }, 0)
|
||||
fmt.Println(leftResult) // -6
|
||||
|
||||
// Building a list in order
|
||||
buildList := u.FoldRight(nums, []int{}, func(n int, acc []int) []int {
|
||||
return append([]int{n}, acc...)
|
||||
})
|
||||
fmt.Println(buildList) // [1, 2, 3]
|
||||
|
||||
// String concatenation
|
||||
words := []string{"a", "b", "c"}
|
||||
concat := u.FoldRight(words, "", func(s, acc string) string { return s + acc })
|
||||
fmt.Println(concat) // "abc"
|
||||
}
|
||||
```
|
||||
32
docs/content/collections/init.md
Normal file
32
docs/content/collections/init.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
title: "Init"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`Init` returns all elements except the last one, and the last element separately. Returns an empty slice and zero value if the input slice is empty. Useful for destructuring lists from the right.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
init, last := u.Init(nums)
|
||||
fmt.Println(init) // [1, 2, 3, 4]
|
||||
fmt.Println(last) // 5
|
||||
|
||||
// Single element
|
||||
single, val := u.Init([]int{42})
|
||||
fmt.Println(single) // []
|
||||
fmt.Println(val) // 42
|
||||
|
||||
// Empty slice
|
||||
empty, zero := u.Init([]int{})
|
||||
fmt.Println(empty) // []
|
||||
fmt.Println(zero) // 0
|
||||
}
|
||||
```
|
||||
28
docs/content/collections/intersperse.md
Normal file
28
docs/content/collections/intersperse.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
title: "Intersperse"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`Intersperse` inserts a separator between each element of the slice. Returns an empty slice if the input is empty. Returns the original element if the input has only one element.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
fmt.Println(u.Intersperse(nums, 0)) // [1, 0, 2, 0, 3, 0, 4, 0, 5]
|
||||
|
||||
// Useful for formatting
|
||||
words := []string{"apple", "banana", "cherry"}
|
||||
fmt.Println(u.Intersperse(words, ",")) // ["apple", ",", "banana", ",", "cherry"]
|
||||
|
||||
// Single element - no separator added
|
||||
single := []int{42}
|
||||
fmt.Println(u.Intersperse(single, 0)) // [42]
|
||||
}
|
||||
```
|
||||
51
docs/content/collections/parallel_reduce.md
Normal file
51
docs/content/collections/parallel_reduce.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
title: "ParallelReduce"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`ParallelReduce` applies a reduction function in parallel using a worker pool. The operation must be associative and commutative for correct results. If workers <= 0, defaults to GOMAXPROCS. On error, the first error is returned and processing is canceled.
|
||||
|
||||
**Note:** This is an experimental function. Order of operations is not guaranteed, so use only with associative and commutative operations (like addition, multiplication, min, max).
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
ctx := context.Background()
|
||||
|
||||
// Parallel sum (safe - addition is associative and commutative)
|
||||
result, err := u.ParallelReduce(ctx, nums, 4, func(ctx context.Context, n int, acc int) (int, error) {
|
||||
// Simulate expensive computation
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return n + acc, nil
|
||||
}, 0)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(result) // Result will vary due to parallel execution
|
||||
|
||||
// With context cancellation
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
_, err = u.ParallelReduce(ctx, nums, 4, func(ctx context.Context, n int, acc int) (int, error) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return n + acc, nil
|
||||
}, 0)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Operation was cancelled:", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Warning:** Do not use ParallelReduce for non-associative operations like subtraction or division, as the results will be unpredictable due to parallel execution order.
|
||||
43
docs/content/collections/replicate.md
Normal file
43
docs/content/collections/replicate.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: "Replicate"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`Replicate` creates a slice containing count copies of value. Returns an empty slice if count is less than or equal to 0. Useful for initialization and testing.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Basic usage
|
||||
fmt.Println(u.Replicate(3, "hello"))
|
||||
// ["hello", "hello", "hello"]
|
||||
|
||||
// Numbers
|
||||
fmt.Println(u.Replicate(5, 0))
|
||||
// [0, 0, 0, 0, 0]
|
||||
|
||||
// Zero count
|
||||
fmt.Println(u.Replicate(0, 42))
|
||||
// []
|
||||
|
||||
// Negative count
|
||||
fmt.Println(u.Replicate(-5, "x"))
|
||||
// []
|
||||
|
||||
// Use case: initialize with default values
|
||||
defaultScores := u.Replicate(10, 100)
|
||||
fmt.Println(defaultScores)
|
||||
// [100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
|
||||
|
||||
// Use case: creating separators
|
||||
separator := u.Replicate(40, "-")
|
||||
fmt.Println(u.Reduce(separator, func(s, acc string) string { return acc + s }, ""))
|
||||
// ----------------------------------------
|
||||
}
|
||||
```
|
||||
37
docs/content/collections/scan.md
Normal file
37
docs/content/collections/scan.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
title: "Scan"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`Scan` is like Reduce but returns all intermediate accumulator values. Also known as prefix scan or cumulative fold. Useful for tracking running totals, running maximums, or other cumulative operations.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Running sum
|
||||
nums := []int{1, 2, 3, 4}
|
||||
add := func(acc, n int) int { return acc + n }
|
||||
fmt.Println(u.Scan(nums, 0, add)) // [1, 3, 6, 10]
|
||||
|
||||
// Running maximum
|
||||
values := []int{3, 1, 4, 1, 5, 9, 2}
|
||||
max := func(acc, n int) int {
|
||||
if n > acc {
|
||||
return n
|
||||
}
|
||||
return acc
|
||||
}
|
||||
fmt.Println(u.Scan(values, 0, max)) // [3, 3, 4, 4, 5, 9, 9]
|
||||
|
||||
// String concatenation
|
||||
words := []string{"hello", "world", "!"}
|
||||
concat := func(acc, s string) string { return acc + s }
|
||||
fmt.Println(u.Scan(words, "", concat)) // ["hello", "helloworld", "helloworld!"]
|
||||
}
|
||||
```
|
||||
43
docs/content/collections/sliding.md
Normal file
43
docs/content/collections/sliding.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: "Sliding"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`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 or greater than the slice length. Useful for moving averages, n-grams, and pattern matching.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
fmt.Println(u.Sliding(nums, 3)) // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
|
||||
|
||||
// Size 2
|
||||
fmt.Println(u.Sliding(nums, 2)) // [[1, 2], [2, 3], [3, 4], [4, 5]]
|
||||
|
||||
// N-grams for text
|
||||
words := []string{"the", "quick", "brown", "fox"}
|
||||
bigrams := u.Sliding(words, 2)
|
||||
fmt.Println(bigrams) // [["the", "quick"], ["quick", "brown"], ["brown", "fox"]]
|
||||
|
||||
// Moving average example
|
||||
data := []int{10, 20, 30, 40, 50}
|
||||
windows := u.Sliding(data, 3)
|
||||
for _, window := range windows {
|
||||
sum := 0
|
||||
for _, v := range window {
|
||||
sum += v
|
||||
}
|
||||
avg := sum / len(window)
|
||||
fmt.Printf("Window: %v, Average: %d\n", window, avg)
|
||||
}
|
||||
// Window: [10 20 30], Average: 20
|
||||
// Window: [20 30 40], Average: 30
|
||||
// Window: [30 40 50], Average: 40
|
||||
}
|
||||
```
|
||||
25
docs/content/collections/takewhile.md
Normal file
25
docs/content/collections/takewhile.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
title: "TakeWhile"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`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.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
lessThan5 := func(n int) bool { return n < 5 }
|
||||
fmt.Println(u.TakeWhile(nums, lessThan5)) // [1, 2, 3, 4]
|
||||
|
||||
words := []string{"apple", "banana", "cherry", "date"}
|
||||
shortWords := func(s string) bool { return len(s) < 6 }
|
||||
fmt.Println(u.TakeWhile(words, shortWords)) // ["apple"]
|
||||
}
|
||||
```
|
||||
47
docs/content/collections/tap.md
Normal file
47
docs/content/collections/tap.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
title: "Tap"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`Tap` applies a function to each element for side effects (like debugging or logging) and returns the original slice unchanged. Useful for debugging pipelines without breaking the flow.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Debugging a pipeline
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
result := u.Tap(
|
||||
u.Map(
|
||||
u.Filter(nums, func(n int) bool { return n%2 == 0 }),
|
||||
func(n int) int { return n * 2 },
|
||||
),
|
||||
func(n int) {
|
||||
fmt.Printf("Debug: %d\n", n) // Prints each value
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Println(result) // [4, 8]
|
||||
|
||||
// Counting elements that pass through
|
||||
count := 0
|
||||
filtered := u.Tap(
|
||||
u.Filter(nums, func(n int) bool { return n > 2 }),
|
||||
func(n int) { count++ },
|
||||
)
|
||||
fmt.Printf("Found %d elements: %v\n", count, filtered)
|
||||
// Found 3 elements: [3 4 5]
|
||||
|
||||
// Logging transformations
|
||||
data := []string{"hello", "world"}
|
||||
u.Tap(data, func(s string) {
|
||||
fmt.Printf("Processing: %s\n", s)
|
||||
})
|
||||
}
|
||||
```
|
||||
45
docs/content/collections/transpose.md
Normal file
45
docs/content/collections/transpose.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
title: "Transpose"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`Transpose` flips a matrix over its diagonal, swapping rows and columns. Returns an empty slice if the input is empty. Assumes all rows have the same length (uses the length of the first row).
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 2x3 matrix becomes 3x2 matrix
|
||||
matrix := [][]int{
|
||||
{1, 2, 3},
|
||||
{4, 5, 6},
|
||||
}
|
||||
transposed := u.Transpose(matrix)
|
||||
fmt.Println(transposed)
|
||||
// [[1, 4], [2, 5], [3, 6]]
|
||||
|
||||
// Square matrix
|
||||
square := [][]int{
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
}
|
||||
fmt.Println(u.Transpose(square))
|
||||
// [[1, 3], [2, 4]]
|
||||
|
||||
// Use case: converting rows to columns for processing
|
||||
data := [][]string{
|
||||
{"Name", "Age", "City"},
|
||||
{"Alice", "30", "NYC"},
|
||||
{"Bob", "25", "LA"},
|
||||
}
|
||||
byColumn := u.Transpose(data)
|
||||
fmt.Println("Names:", byColumn[0]) // [Name Alice Bob]
|
||||
fmt.Println("Ages:", byColumn[1]) // [Age 30 25]
|
||||
fmt.Println("Cities:", byColumn[2]) // [City NYC LA]
|
||||
}
|
||||
```
|
||||
43
docs/content/collections/unzip.md
Normal file
43
docs/content/collections/unzip.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: "Unzip"
|
||||
date: 2025-01-16T00:00:00-00:00
|
||||
---
|
||||
|
||||
`Unzip` splits a slice of tuples into two separate slices. The inverse operation of Zip. Useful for separating paired data.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
u "github.com/rjNemo/underscore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Basic usage
|
||||
pairs := []u.Tuple[int, string]{
|
||||
{Left: 1, Right: "a"},
|
||||
{Left: 2, Right: "b"},
|
||||
{Left: 3, Right: "c"},
|
||||
}
|
||||
|
||||
nums, letters := u.Unzip(pairs)
|
||||
fmt.Println(nums) // [1, 2, 3]
|
||||
fmt.Println(letters) // ["a", "b", "c"]
|
||||
|
||||
// Use case: separating keys and values
|
||||
keyValuePairs := []u.Tuple[string, int]{
|
||||
{Left: "apple", Right: 5},
|
||||
{Left: "banana", Right: 3},
|
||||
{Left: "cherry", Right: 8},
|
||||
}
|
||||
|
||||
items, counts := u.Unzip(keyValuePairs)
|
||||
fmt.Println("Items:", items) // Items: [apple banana cherry]
|
||||
fmt.Println("Counts:", counts) // Counts: [5 3 8]
|
||||
|
||||
// Empty slice
|
||||
emptyNums, emptyStrs := u.Unzip([]u.Tuple[int, string]{})
|
||||
fmt.Println(emptyNums, emptyStrs) // [] []
|
||||
}
|
||||
```
|
||||
Loading…
Reference in a new issue