Documentation
¶
Overview ¶
Package iterx provides lazy, context-aware sequence transformations for Go 1.23 iter.Seq values.
The iterx package is useful when you want to build allocation-light data pipelines over slices, files, database rows, or generated streams without materializing every intermediate result. Each helper returns a new lazy sequence or consumes one directly, so work only happens when the caller ranges over the final sequence.
Example:
ctx := context.Background()
numbers := slices.Values([]int{1, 2, 3, 4, 5})
for v := range iterx.Filter(ctx, numbers, func(n int) bool {
return n%2 == 0
}) {
fmt.Println(v)
}
The iterx package functions are lazy and stop as soon as the context is cancelled or the consumer stops reading. Reverse buffers the full sequence in memory before yielding results, while helpers like Filter, Map, and Take stream values one at a time.
Index ¶
- func Chunk[T any](ctx context.Context, seq iter.Seq[T], n int) iter.Seq[[]T]
- func Contains[T comparable](ctx context.Context, seq iter.Seq[T], target T) bool
- func Distinct[T comparable](ctx context.Context, seq iter.Seq[T]) iter.Seq[T]
- func Drain[T any](ctx context.Context, seq iter.Seq[T], fn func(T) error) error
- func Filter[T any](ctx context.Context, seq iter.Seq[T], fn func(T) bool) iter.Seq[T]
- func FlatMap[T, U any](ctx context.Context, seq iter.Seq[T], fn func(T) iter.Seq[U]) iter.Seq[U]
- func Flatten[T any](ctx context.Context, seq iter.Seq[[]T]) iter.Seq[T]
- func ForEach[T any](ctx context.Context, seq iter.Seq[T], fn func(T))
- func Map[T, U any](ctx context.Context, seq iter.Seq[T], fn func(T) U) iter.Seq[U]
- func Reverse[T any](ctx context.Context, seq iter.Seq[T]) iter.Seq[T]
- func Take[T any](ctx context.Context, seq iter.Seq[T], n int) iter.Seq[T]
- func TakeWhile[T any](ctx context.Context, seq iter.Seq[T], fn func(T) bool) iter.Seq[T]
- func Validate[T any](ctx context.Context, seq iter.Seq[T], fn func(T) (bool, string), ...) iter.Seq[T]
- func Zip[A, B any](ctx context.Context, a iter.Seq[A], b iter.Seq[B]) iter.Seq[[2]any]
- type ValidationError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Chunk ¶
Chunk splits a sequence into slices of size n.
Example ¶
ExampleChunk demonstrates how to process data in batches, such as for bulk database inserts or writing to chunked APIs.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
logs := slices.Values([]string{"log1", "log2", "log3", "log4", "log5"})
for batch := range iterx.Chunk(ctx, logs, 2) {
fmt.Printf("Batch size: %d, items: %v\n", len(batch), batch)
}
}
Output: Batch size: 2, items: [log1 log2] Batch size: 2, items: [log3 log4] Batch size: 1, items: [log5]
func Contains ¶
Contains returns true if the sequence contains the target value.
Example ¶
ExampleContains demonstrates checking if an item exists efficiently, as the iterator stops processing as soon as it finds a match.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
users := slices.Values([]string{"alice", "bob", "charlie", "admin"})
hasAdmin := iterx.Contains(ctx, users, "admin")
fmt.Println("Has admin:", hasAdmin)
}
Output: Has admin: true
func Distinct ¶
Distinct filters out duplicate values keeping only the first occurrence.
Example ¶
ExampleDistinct demonstrates removing duplicates from an incoming stream, such as a sequence of IP addresses.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
ipAddresses := slices.Values([]string{"192.168.1.1", "10.0.0.1", "192.168.1.1", "10.0.0.2"})
for ip := range iterx.Distinct(ctx, ipAddresses) {
fmt.Println(ip)
}
}
Output: 192.168.1.1 10.0.0.1 10.0.0.2
func Drain ¶
Drain consumes a sequence and calls fn for each item. Stops immediately if ctx is cancelled or fn returns an error. Use Drain when your terminal operation can fail — writing to CSV, DB, files. Use ForEach when your terminal operation cannot fail — logging, printing.
example:
err := iterx.Drain(ctx, users, func(u User) error {
return csvWriter.Write([]string{u.Name, u.Email})
})
Example ¶
ExampleDrain demonstrates exhausting a sequence when the terminal operation can fail, like writing to an io.Writer.
package main
import (
"context"
"fmt"
"slices"
"strings"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
lines := slices.Values([]string{"header", "data 1", "data 2"})
// Simulate writing to something that could fail
var out strings.Builder
err := iterx.Drain(ctx, lines, func(line string) error {
_, err := out.WriteString(line + "\n")
return err // stops early if err != nil
})
fmt.Printf("Error: %v, Output:\n%s", err, out.String())
}
Output: Error: <nil>, Output: header data 1 data 2
func Filter ¶
Filter returns a new sequence containing only elements where fn returns true.
Example ¶
ExampleFilter demonstrates removing unwanted items from a stream.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
numbers := slices.Values([]int{1, 2, 3, 4, 5, 6})
evens := iterx.Filter(ctx, numbers, func(n int) bool {
return n%2 == 0
})
for v := range evens {
fmt.Println(v)
}
}
Output: 2 4 6
func FlatMap ¶
FlatMap transforms each element into a sequence, then flattens all sequences.
Example ¶
ExampleFlatMap demonstrates expanding a single item into multiple items. In this case: expanding a user into a stream of their roles.
package main
import (
"context"
"fmt"
"iter"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
type DemoUser struct {
Name string
Roles []string
}
func main() {
ctx := context.Background()
users := slices.Values([]DemoUser{
{Name: "Alice", Roles: []string{"admin", "editor"}},
{Name: "Bob", Roles: []string{"viewer"}},
})
roles := iterx.FlatMap(ctx, users, func(u DemoUser) iter.Seq[string] {
return slices.Values(u.Roles)
})
for role := range roles {
fmt.Println(role)
}
}
Output: admin editor viewer
func Flatten ¶
Flatten converts a sequence of slices into a flat sequence of elements.
Example ¶
ExampleFlatten demonstrates unrolling an iterator of slices into a flat iterator.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
batches := slices.Values([][]int{
{1, 2},
{3},
{4, 5, 6},
})
for v := range iterx.Flatten(ctx, batches) {
fmt.Println(v)
}
}
Output: 1 2 3 4 5 6
func ForEach ¶
ForEach calls fn for every element in the sequence.
Example ¶
ExampleForEach demonstrates running a non-failing side effect on every item.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
messages := slices.Values([]string{"hello", "world"})
iterx.ForEach(ctx, messages, func(msg string) {
fmt.Println("Processed:", msg)
})
}
Output: Processed: hello Processed: world
func Map ¶
Map transforms each element using fn.
Example ¶
ExampleMap demonstrates transforming items from one type to another.
package main
import (
"context"
"fmt"
"slices"
"strings"
"github.com/MostafaMagdSalama/vortex/iterx"
)
type DemoUser struct {
Name string
Roles []string
}
func main() {
ctx := context.Background()
users := slices.Values([]DemoUser{
{Name: "Alice"},
{Name: "Bob"},
})
names := iterx.Map(ctx, users, func(u DemoUser) string {
return strings.ToUpper(u.Name) // U is string
})
for name := range names {
fmt.Println(name)
}
}
Output: ALICE BOB
func Reverse ¶
Reverse collects the sequence into memory and yields it in reverse order.
Example ¶
ExampleReverse demonstrates yielding the sequence in reverse order.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
steps := slices.Values([]string{"step 1", "step 2", "step 3"})
for step := range iterx.Reverse(ctx, steps) {
fmt.Println(step)
}
}
Output: step 3 step 2 step 1
func Take ¶
Take returns the first n elements.
Example ¶
ExampleTake demonstrates grabbing just the first N elements. Particularly useful with endless streams or large files.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
items := slices.Values([]int{10, 20, 30, 40, 50, 60})
top3 := iterx.Take(ctx, items, 3)
for v := range top3 {
fmt.Println(v)
}
}
Output: 10 20 30
func TakeWhile ¶
TakeWhile yields values from the sequence as long as fn returns true. Iteration stops as soon as fn evaluates to false for the first time.
Example ¶
ExampleTakeWhile demonstrates reading a stream until a condition stops being met. For instance: reading lines from a log until a marker is reached.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
logs := slices.Values([]string{"ok", "ok", "ok", "error", "ok"})
for v := range iterx.TakeWhile(ctx, logs, func(s string) bool {
return s != "error"
}) {
fmt.Println(v)
}
}
Output: ok ok ok
func Validate ¶
func Validate[T any](ctx context.Context, seq iter.Seq[T], fn func(T) (bool, string), onError func(ValidationError[T])) iter.Seq[T]
Validate conditionally streams elements by evaluating fn(item). If fn yields {false, reason}, the onError callback is triggered with a ValidationError, and the element is discarded from the resulting sequence.
Example ¶
ExampleValidate demonstrates checking structures and extracting validation metadata on failing entries without stopping the processing pipeline.
package main
import (
"context"
"fmt"
"slices"
"strings"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
emails := slices.Values([]string{"test@example.com", "invalid-email", "hello@world.com"})
valid := iterx.Validate(ctx, emails,
func(email string) (bool, string) {
if !strings.Contains(email, "@") {
return false, "missing @"
}
return true, ""
},
func(err iterx.ValidationError[string]) {
fmt.Printf("Validation error: %s - %s\n", err.Item, err.Reason)
},
)
for email := range valid {
fmt.Println("Processed valid email:", email)
}
}
Output: Processed valid email: test@example.com Validation error: invalid-email - missing @ Processed valid email: hello@world.com
func Zip ¶
Zip combines two sequences into pairs, yielding [2]any{a, b} for each corresponding element. It stops as soon as the shortest sequence runs out.
Example ¶
ExampleZip demonstrates combining two synchronised streams into pairs.
package main
import (
"context"
"fmt"
"slices"
"github.com/MostafaMagdSalama/vortex/iterx"
)
func main() {
ctx := context.Background()
keys := slices.Values([]string{"A", "B", "C"})
values := slices.Values([]int{100, 200}) // Notice: shorter sequence!
for pair := range iterx.Zip(ctx, keys, values) {
fmt.Printf("%v: %v\n", pair[0], pair[1])
}
}
Output: A: 100 B: 200
Types ¶
type ValidationError ¶
ValidationError represents an item that failed validation.