diff --git a/CLAUDE.md b/CLAUDE.md index 8876495..980ef65 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,4 +27,4 @@ go install honnef.co/go/tools/cmd/staticcheck@latest && staticcheck ./... **Testing**: All tests are `Example` functions — they serve as both documentation and regression tests. No traditional unit tests. Run a single example with `go test -run ExampleFunctionName`. -**Commit tags**: Commit messages use `#patch`, `#minor`, `#major`, or `#none` suffixes for automated semantic version bumping. +**Commit tags**: Commit messages use `#patch`, `#minor`, `#major`, or `#none` suffixes for automated semantic version bumping. Changing the Go version in `go.mod` should use `#minor`. diff --git a/README.md b/README.md index a6170de..7726a8a 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,8 @@ Golang's "missing" iterator/sequence functions. * `FindBy(iter.Seq[T], func(T) bool) (T, int, bool)`: Returns the first value for which the function returns true * `FindByKey(iter.Seq2[K,V], K) (V, int, bool)`: Returns the value of the first key-value pair with the given key * `FindByValue(iter.Seq2[K,V], V) (K, int, bool)`: Returns the key of the first key-value pair with the given value +* `At(iter.Seq[T], int) (T, bool)`: Returns the value at the given 0-based index, or zero value and false if out of range +* `AtKV(iter.Seq2[K,V], int) (K, V, bool)`: Returns the key and value at the given 0-based index, or zero values and false if out of range ## Utility Functions diff --git a/go.mod b/go.mod index a13e4ce..4f20210 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/freeformz/seq go 1.25 + +retract v0.2.3 diff --git a/seq.go b/seq.go index 658bb9a..7396311 100644 --- a/seq.go +++ b/seq.go @@ -941,6 +941,49 @@ func MapToKV[T, K, V any](seq iter.Seq[T], fn func(T) (K, V)) iter.Seq2[K, V] { } } +// At returns the value at the given 0-based index in the sequence and true. If +// the index is out of range (negative or beyond sequence length), it returns +// the zero value and false. The provided sequence is iterated over up to and +// including the target index when At is called. +func At[T any](seq iter.Seq[T], index int) (T, bool) { + if index < 0 { + var z T + return z, false + } + var i int + for v := range seq { + if i == index { + return v, true + } + i++ + } + var z T + return z, false +} + +// AtKV returns the key and value at the given 0-based index in the sequence and true. If +// the index is out of range (negative or beyond sequence length), it returns +// the zero values and false. The provided sequence is iterated over up to and +// including the target index when AtKV is called. To find a value by key, use +// FindByKey instead. +func AtKV[K any, V any](seq iter.Seq2[K, V], index int) (K, V, bool) { + if index < 0 { + var zk K + var zv V + return zk, zv, false + } + var i int + for k, v := range seq { + if i == index { + return k, v, true + } + i++ + } + var zk K + var zv V + return zk, zv, false +} + // Find returns the index of the first occurrence of the value in the sequence, the "index" (0 based) of the value, and true. If // the value is not found, the first return value is the length of the sequence, the second return value is false. The provided // sequence is iterated over when Find is called. diff --git a/seq_test.go b/seq_test.go index f5ea771..15a0e30 100644 --- a/seq_test.go +++ b/seq_test.go @@ -1026,6 +1026,80 @@ func ExampleMapToKV() { // C 3 } +func ExampleAt() { + i := With(1, 2, 3, 4, 5) + + fmt.Println(At(i, 2)) + + // Output: + // 3 true +} + +func ExampleAt_outOfRange() { + i := With(1, 2, 3) + + fmt.Println(At(i, 5)) + + // Output: + // 0 false +} + +func ExampleAt_empty() { + i := With[int]() + + fmt.Println(At(i, 0)) + + // Output: + // 0 false +} + +func ExampleAt_negative() { + i := With(1, 2, 3) + + fmt.Println(At(i, -1)) + + // Output: + // 0 false +} + +func ExampleAtKV() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + + fmt.Println(AtKV(i, 1)) + + // Output: + // b 2 true +} + +func ExampleAtKV_outOfRange() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}) + + fmt.Println(AtKV(i, 5)) + + // Output: + // 0 false +} + +func ExampleAtKV_negative() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}) + + fmt.Println(AtKV(i, -1)) + + // Output: + // 0 false +} + +func ExampleAtKV_empty() { + i := WithKV[string, int]() + + fmt.Println(AtKV(i, 0)) + + // Output: + // 0 false +} func ExampleFind() { i := With(1, 2, 3, 4, 5)