stream

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 28, 2026 License: MIT Imports: 3 Imported by: 0

README

stream

Go Reference CI coverage Go Report Card Go Version Go Playground

English | 日本語 | 中文 | 한국어 | Español | Português

A Go generic stream processing library. Chainable collection operations for filter, map, sort, group, and more — with lazy evaluation by default. Try it online!

All operations are lazy. Operations that require full data (Sort, Reverse, Shuffle, TakeLast, Chunk, Partition) buffer internally and resume lazy iteration automatically.

Requirements

  • Go 1.23 or later (uses iter.Seq[T] and range-over-function)

Install

go get github.com/nd-forge/stream

Quick Start

import "github.com/nd-forge/stream"

// Method chaining for same-type operations
result := stream.Of(5, 2, 8, 1, 9, 4, 7, 3, 6).
    Filter(func(n int) bool { return n%2 == 0 }).
    Sort(func(a, b int) int { return b - a }).
    Take(3).
    ToSlice()
// [8, 6, 4]

// Top-level functions for type-changing operations
names := stream.Map(
    stream.Of(users...).Filter(func(u User) bool { return u.IsActive }),
    func(u User) string { return u.Name },
).ToSlice()

Design

Lazy by Default

All operations build a lazy pipeline internally using iter.Seq[T]. No intermediate slices are allocated until a terminal operation (ToSlice, ForEach, Reduce, etc.) is called.

Operations that inherently need all data — Sort, Reverse, Shuffle, TakeLast, Chunk, Partition — buffer internally, then resume lazy iteration for subsequent operations.

Type Parameters

Go does not allow methods to introduce new type parameters. This library separates:

Kind Implementation Signature
Type-preserving (Filter, Sort, Take...) Methods — chainable Stream[T] → Stream[T]
Type-changing (Map, FlatMap, GroupBy...) Top-level functions Stream[T] → Stream[U]

API

Constructors
Function Description
Of[T](items ...T) Create from variadic args
From[T](items []T) Create from slice (copies)
Range(start, end) Create integer sequence [start, end)
Generate[T](n, fn) Create n elements with generator
Generators (Infinite Sequences)
Function Description
Naturals() 0, 1, 2, 3, ...
Iterate(seed, fn) seed, fn(seed), fn(fn(seed)), ...
Repeat(value) Infinite repetition of value
RepeatN(value, n) Repeat value n times
Chainable Methods

Operations that return Stream[T] and can be chained.

Method Description
Filter(predicate) Keep elements matching predicate
Reject(predicate) Remove elements matching predicate
Sort(cmp) Sort by comparison function
Reverse() Reverse order
Take(n) / TakeLast(n) First / last n elements
Skip(n) Remove first n elements
TakeWhile(pred) / DropWhile(pred) Take / skip from start while true
Distinct(key) Remove duplicates by key
Shuffle() Random order
Peek(fn) Execute side effect without modifying
Chain(others...) Concatenate multiple streams

Sort, Reverse, Shuffle, TakeLast buffer all elements internally.

Terminal Operations
Method Returns
ToSlice() []T
First() / Last() (T, bool)
Find(predicate) (T, bool)
Reduce(initial, fn) T
Any(pred) / All(pred) / None(pred) bool
Count() / CountBy(pred) int
IsEmpty() bool
Contains(predicate) bool
MinBy(less) / MaxBy(less) (T, bool)
Partition(pred) (Stream[T], Stream[T])
Chunk(size) []Stream[T]
ForEach(fn) / ForEachIndexed(fn)
Seq() iter.Seq[T]
Transform Functions

Top-level functions for type-changing operations.

Function Description
Map(s, fn) Transform T → U
MapIndexed(s, fn) Transform with index
FlatMap(s, fn) Transform and flatten T → []U
Reduce(s, initial, fn) Fold into different type T → U
GroupBy(s, key) Group by key → map[K][]T
Associate(s, fn) Build map → map[K]V
Zip(s1, s2) Pair two streams → Stream[Pair[T,U]]
Flatten(s) Flatten Stream[[]T] → Stream[T]
ToMap(s) Convert Stream[Pair[K,V]] → map[K]V
Enumerate(s) Add index → Stream[Pair[int,T]]
Numeric Functions

Specialized operations for numeric streams (int, float64, etc.).

Function Description
Sum(s) / Avg(s) Sum / average
Min(s) / Max(s) Minimum / maximum
SumBy(s, fn) / AvgBy(s, fn) Sum / average of extracted values
iter.Seq Bridge
Function Description
Seq() Stream[T]iter.Seq[T]
Collect(seq) iter.Seq[T]Stream[T]
Collect2(seq) iter.Seq2[K,V]Stream[Pair[K,V]]

Examples

Types used in examples below:

type Product struct {
    Name     string
    Category string
    Price    float64
    InStock  bool
}

type User struct {
    Name     string
    Age      int
    IsActive bool
    Orders   []Order
}

type Order struct {
    Product  string
    Amount   float64
    Discount float64
}
Filter, Sort, Take [playground]
products := stream.Of(
    Product{Name: "Laptop", Category: "Electronics", Price: 1200, InStock: true},
    Product{Name: "T-Shirt", Category: "Clothing", Price: 25, InStock: true},
    Product{Name: "Headphones", Category: "Electronics", Price: 150, InStock: false},
    Product{Name: "Jeans", Category: "Clothing", Price: 80, InStock: true},
)

// In-stock products, sorted by price descending, top 3
top3 := products.
    Filter(func(p Product) bool { return p.InStock }).
    Sort(func(a, b Product) int {
        if a.Price > b.Price { return -1 }
        if a.Price < b.Price { return 1 }
        return 0
    }).
    Take(3).
    ToSlice()
Map and FlatMap [playground]
// Extract product names
names := stream.Map(
    products.Filter(func(p Product) bool { return p.InStock }),
    func(p Product) string { return p.Name },
).ToSlice()

// Flatten nested orders from users
allOrders := stream.FlatMap(
    stream.Of(users...),
    func(u User) []Order { return u.Orders },
)
GroupBy and Aggregate [playground]
byCategory := stream.GroupBy(products, func(p Product) string { return p.Category })

for category, group := range byCategory {
    total := stream.SumBy(stream.Of(group...), func(p Product) float64 { return p.Price })
    fmt.Printf("%s: total=$%.2f count=%d\n", category, total, len(group))
}
Partition and Chunk [playground]
// Split by condition
inStock, outOfStock := products.Partition(func(p Product) bool { return p.InStock })

// Batch processing
batches := stream.From(items).Chunk(100)
for _, batch := range batches {
    api.Send(batch.ToSlice())
}
Zip [playground]
names := stream.Of("Alice", "Bob", "Charlie")
scores := stream.Of(85.0, 92.0, 78.0)

pairs := stream.Zip(names, scores).ToSlice()
// [{Alice 85}, {Bob 92}, {Charlie 78}]
Infinite Sequences [playground]
// First 5 even natural numbers
evens := stream.Naturals().
    Filter(func(n int) bool { return n%2 == 0 }).
    Take(5).
    ToSlice()
// [0, 2, 4, 6, 8]

// Fibonacci sequence
fib := stream.Map(
    stream.Iterate(
        stream.Pair[int, int]{First: 0, Second: 1},
        func(p stream.Pair[int, int]) stream.Pair[int, int] {
            return stream.Pair[int, int]{First: p.Second, Second: p.First + p.Second}
        },
    ).Take(10),
    func(p stream.Pair[int, int]) int { return p.First },
).ToSlice()
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

// Lazy evaluation: processes only 2,001 of 1,000,000 elements
result := stream.Range(0, 1_000_000).
    Filter(func(n int) bool { return n%1000 == 0 }).
    Take(3).
    ToSlice()
// [0, 1000, 2000]
iter.Seq Bridge [playground]
// Standard library interop
keys := stream.Collect(maps.Keys(myMap)).Sort(cmp).ToSlice()

// for-range support
for v := range stream.Of(1, 2, 3).Seq() {
    fmt.Println(v)
}

Benchmark

10,000 int elements — Apple M1. Compared with samber/lo.

Filter + Take (lazy evaluation advantage)
Benchmark                    ns/op       B/op    allocs/op
────────────────────────────────────────────────────────────
Native                         111        248          5
Stream                         346        528         15   ← 3.1x native
lo                          25,000     81,920          1   ← 72x slower than Stream

Stream's lazy evaluation short-circuits — only processes elements until Take is satisfied. lo must filter all 10,000 elements first, then take.

Chained: Filter → Map → Take 5
Benchmark                    ns/op       B/op    allocs/op
────────────────────────────────────────────────────────────
Native                         171        260          9
Stream                         384        544         19   ← 2.2x native
lo                          64,600    152,601      3,336   ← 168x slower than Stream
Full scan (no early termination)
Benchmark                    ns/op       B/op    allocs/op
────────────────────────────────────────────────────────────
Native Filter              19,400    128,249         16
lo     Filter              25,600     81,920          1
Stream Filter              42,200    128,409         22

Native Reduce               3,300          0          0
lo     Reduce               9,700          0          0
Stream Reduce              23,400         64          2

For full scans without early termination, lo is faster than Stream due to lower abstraction overhead. Stream's advantage shines in pipelines with Take, First, Find, or other short-circuiting operations.

Run cd _benchmark && go test -bench=. -benchmem ./... to reproduce.

License

MIT

Documentation

Overview

Package stream provides a chainable collection operations library for Go.

Stream[T] wraps an iter.Seq[T] and provides lazy evaluation with method chaining for filter, sort, take, skip, and other operations that preserve the element type. For type-changing operations (Map, FlatMap, Zip), use top-level functions.

All operations are lazy by default. Operations that require full data (Sort, Reverse, Shuffle, TakeLast, Chunk, Partition) buffer internally and resume lazy iteration.

Usage:

// Method chaining for same-type operations
result := stream.Of(1, 2, 3, 4, 5).
    Filter(func(n int) bool { return n%2 == 0 }).
    Sort(func(a, b int) int { return a - b }).
    Take(3).
    ToSlice()

// Top-level functions for type-changing operations
names := stream.Map(
    stream.Of(users...).Filter(func(u User) bool { return u.IsActive }),
    func(u User) string { return u.Name },
).ToSlice()
Example (Chaining)
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	// Method chaining: filter even numbers, sort, take top 3
	result := stream.Of(9, 2, 7, 4, 1, 8, 3, 6, 5).
		Filter(func(n int) bool { return n%2 == 0 }).
		Sort(func(a, b int) int { return a - b }).
		Take(3).
		ToSlice()
	fmt.Println(result)
}
Output:

[2 4 6]
Example (Fibonacci)
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	// Fibonacci sequence using Iterate with Pair
	fib := stream.Map(
		stream.Iterate(
			stream.Pair[int, int]{First: 0, Second: 1},
			func(p stream.Pair[int, int]) stream.Pair[int, int] {
				return stream.Pair[int, int]{First: p.Second, Second: p.First + p.Second}
			},
		).Take(10),
		func(p stream.Pair[int, int]) int { return p.First },
	).ToSlice()
	fmt.Println(fib)
}
Output:

[0 1 1 2 3 5 8 13 21 34]
Example (LazyFilterTake)
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	// Lazy evaluation: only evaluates elements as needed
	result := stream.Naturals().
		Filter(func(n int) bool { return n%2 == 0 }).
		Take(5).
		ToSlice()
	fmt.Println(result)
}
Output:

[0 2 4 6 8]
Example (TextProcessing)
package main

import (
	"fmt"
	"strings"

	"github.com/nd-forge/stream"
)

func main() {
	words := stream.Of("hello", "world", "hello", "go", "stream", "go").
		Distinct(func(s string) string { return s }).
		Sort(func(a, b string) int { return len(a) - len(b) })

	result := stream.Map(words, strings.ToUpper).ToSlice()
	fmt.Println(result)
}
Output:

[GO HELLO WORLD STREAM]

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Associate

func Associate[T any, K comparable, V any](s Stream[T], fn func(T) (K, V)) map[K]V

Associate creates a map from Stream elements using a key-value function.

userMap := stream.Associate(users, func(u User) (int, string) {
    return u.ID, u.Name
})

func Avg

func Avg[T Number](s Stream[T]) float64

Avg returns the average of all elements in a numeric Stream.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	fmt.Println(stream.Avg(stream.Of(10.0, 20.0, 30.0)))
}
Output:

20

func AvgBy

func AvgBy[T any, N Number](s Stream[T], fn func(T) N) float64

AvgBy extracts a numeric value from each element and returns the average.

func GroupBy

func GroupBy[T any, K comparable](s Stream[T], key func(T) K) map[K][]T

GroupBy groups elements by a key function and returns a map of key → slice.

bySymbol := stream.GroupBy(trades, func(t Trade) string { return t.Symbol })
// bySymbol["AUDUSD"] → []Trade

func Max

func Max[T Number](s Stream[T]) (T, bool)

Max returns the maximum element in a numeric Stream.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	v, _ := stream.Max(stream.Of(3, 1, 4, 1, 5))
	fmt.Println(v)
}
Output:

5

func Min

func Min[T Number](s Stream[T]) (T, bool)

Min returns the minimum element in a numeric Stream.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	v, _ := stream.Min(stream.Of(3, 1, 4, 1, 5))
	fmt.Println(v)
}
Output:

1

func Reduce

func Reduce[T, U any](s Stream[T], initial U, fn func(acc U, item T) U) U

Reduce folds all elements into a value of a different type.

total := stream.Reduce(orders, 0.0, func(acc float64, o Order) float64 {
    return acc + o.Amount
})
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	sum := stream.Reduce(
		stream.Of(1, 2, 3, 4, 5),
		0,
		func(acc, v int) int { return acc + v },
	)
	fmt.Println(sum)
}
Output:

15

func Sum

func Sum[T Number](s Stream[T]) T

Sum returns the sum of all elements in a numeric Stream.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	fmt.Println(stream.Sum(stream.Of(1, 2, 3, 4, 5)))
}
Output:

15

func SumBy

func SumBy[T any, N Number](s Stream[T], fn func(T) N) N

SumBy extracts a numeric value from each element and returns the sum.

func ToMap

func ToMap[K comparable, V any](s Stream[Pair[K, V]]) map[K]V

ToMap collects a Stream of Pairs into a map.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	m := stream.ToMap(stream.Zip(
		stream.Of("a", "b", "c"),
		stream.Of(1, 2, 3),
	))
	fmt.Println(m["a"], m["b"], m["c"])
}
Output:

1 2 3

Types

type Number

type Number interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
		~float32 | ~float64
}

Number is a constraint for numeric types.

type Pair

type Pair[T, U any] struct {
	First  T
	Second U
}

Pair holds two values of potentially different types.

type Stream

type Stream[T any] struct {
	// contains filtered or unexported fields
}

Stream is a lazy evaluation wrapper around iter.Seq[T] that supports method chaining. All intermediate operations are deferred until a terminal operation is called. Operations that require full data (Sort, Reverse, Shuffle, TakeLast) buffer internally then resume lazy iteration.

Stream is reusable: calling terminal operations multiple times produces the same result, as the underlying iter.Seq is re-executed each time.

func Collect

func Collect[T any](seq iter.Seq[T]) Stream[T]

Collect creates a Stream[T] from an iter.Seq[T].

// Use with standard library iterators
keys := stream.Collect(maps.Keys(myMap)).Sort(cmp).ToSlice()
Example
package main

import (
	"fmt"
	"maps"

	"github.com/nd-forge/stream"
)

func main() {
	m := map[string]int{"x": 1, "y": 2, "z": 3}
	s := stream.Collect(maps.Values(m)).
		Sort(func(a, b int) int { return a - b }).
		ToSlice()
	fmt.Println(s)
}
Output:

[1 2 3]

func Collect2

func Collect2[K, V any](seq iter.Seq2[K, V]) Stream[Pair[K, V]]

Collect2 creates a Stream[Pair[K,V]] from an iter.Seq2[K,V].

pairs := stream.Collect2(maps.All(myMap))
Example
package main

import (
	"fmt"
	"maps"

	"github.com/nd-forge/stream"
)

func main() {
	m := map[string]int{"a": 1}
	pairs := stream.Collect2(maps.All(m)).ToSlice()
	fmt.Println(pairs[0].First, pairs[0].Second)
}
Output:

a 1

func Enumerate

func Enumerate[T any](s Stream[T]) Stream[Pair[int, T]]

Enumerate wraps each element with its index as a Pair[int, T].

stream.Enumerate(stream.Of("a", "b", "c"))
// yields {0, "a"}, {1, "b"}, {2, "c"}
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Enumerate(stream.Of("a", "b", "c")).ToSlice()
	for _, p := range result {
		fmt.Printf("%d:%s ", p.First, p.Second)
	}
	fmt.Println()
}
Output:

0:a 1:b 2:c

func FlatMap

func FlatMap[T, U any](s Stream[T], fn func(T) []U) Stream[U]

FlatMap lazily transforms each element into a slice and flattens the result.

allOrders := stream.FlatMap(
    stream.Of(users...),
    func(u User) []Order { return u.Orders },
)
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.FlatMap(
		stream.Of([]int{1, 2}, []int{3, 4}),
		func(s []int) []int { return s },
	).ToSlice()
	fmt.Println(result)
}
Output:

[1 2 3 4]

func Flatten

func Flatten[T any](s Stream[[]T]) Stream[T]

Flatten lazily flattens a Stream of slices into a flat Stream.

flat := stream.Flatten(stream.Of([]int{1, 2}, []int{3, 4}))
// yields 1, 2, 3, 4
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Flatten(
		stream.Of([]int{1, 2}, []int{3, 4}, []int{5}),
	).ToSlice()
	fmt.Println(result)
}
Output:

[1 2 3 4 5]

func From

func From[T any](items []T) Stream[T]

From creates a new Stream from an existing slice (copies the slice).

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	data := []string{"Go", "Rust", "Python"}
	result := stream.From(data).
		Filter(func(s string) bool { return len(s) <= 4 }).
		ToSlice()
	fmt.Println(result)
}
Output:

[Go Rust]

func Generate

func Generate[T any](n int, gen func(index int) T) Stream[T]

Generate creates a Stream of n elements using a generator function.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	squares := stream.Generate(5, func(i int) int { return i * i }).ToSlice()
	fmt.Println(squares)
}
Output:

[0 1 4 9 16]

func Iterate

func Iterate[T any](seed T, fn func(T) T) Stream[T]

Iterate creates an infinite Stream: seed, fn(seed), fn(fn(seed)), ...

// Powers of 2: 1, 2, 4, 8, 16, ...
stream.Iterate(1, func(n int) int { return n * 2 }).Take(5)
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	// Powers of 2: 1, 2, 4, 8, 16
	result := stream.Iterate(1, func(n int) int { return n * 2 }).
		Take(5).ToSlice()
	fmt.Println(result)
}
Output:

[1 2 4 8 16]

func Map

func Map[T, U any](s Stream[T], fn func(T) U) Stream[U]

Map lazily transforms each element of type T into type U.

names := stream.Map(
    stream.Of(users...).Filter(func(u User) bool { return u.IsActive }),
    func(u User) string { return u.Name },
).ToSlice()
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Map(
		stream.Of(1, 2, 3),
		func(n int) string { return fmt.Sprintf("item_%d", n) },
	).ToSlice()
	fmt.Println(result)
}
Output:

[item_1 item_2 item_3]

func MapIndexed

func MapIndexed[T, U any](s Stream[T], fn func(int, T) U) Stream[U]

MapIndexed is like Map but also provides the index to the transform function.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.MapIndexed(
		stream.Of("a", "b", "c"),
		func(i int, s string) string { return fmt.Sprintf("%d:%s", i, s) },
	).ToSlice()
	fmt.Println(result)
}
Output:

[0:a 1:b 2:c]

func Naturals

func Naturals() Stream[int]

Naturals returns an infinite Stream of natural numbers: 0, 1, 2, 3, ...

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Naturals().Take(5).ToSlice()
	fmt.Println(result)
}
Output:

[0 1 2 3 4]

func Of

func Of[T any](items ...T) Stream[T]

Of creates a new Stream from variadic arguments.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(5, 2, 8, 1, 9).
		Filter(func(n int) bool { return n%2 == 0 }).
		Sort(func(a, b int) int { return a - b }).
		ToSlice()
	fmt.Println(result)
}
Output:

[2 8]

func Range

func Range(start, end int) Stream[int]

Range creates a Stream of integers from start (inclusive) to end (exclusive).

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Range(1, 6).ToSlice()
	fmt.Println(result)
}
Output:

[1 2 3 4 5]

func Repeat

func Repeat[T any](value T) Stream[T]

Repeat creates an infinite Stream that yields the same value. Must be combined with Take, TakeWhile, or Find to terminate.

zeros := stream.Repeat(0).Take(10).ToSlice()
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Repeat(42).Take(3).ToSlice()
	fmt.Println(result)
}
Output:

[42 42 42]

func RepeatN

func RepeatN[T any](value T, n int) Stream[T]

RepeatN creates a Stream that yields the same value n times.

dashes := stream.RepeatN("-", 20).ToSlice()
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.RepeatN("-", 4).ToSlice()
	fmt.Println(result)
}
Output:

[- - - -]

func Zip

func Zip[T, U any](s1 Stream[T], s2 Stream[U]) Stream[Pair[T, U]]

Zip lazily combines two Streams into a Stream of pairs. Stops when either Stream is exhausted.

pairs := stream.Zip(names, scores)
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	pairs := stream.Zip(
		stream.Of("a", "b", "c"),
		stream.Of(1, 2, 3),
	).ToSlice()
	for _, p := range pairs {
		fmt.Printf("%s=%d ", p.First, p.Second)
	}
	fmt.Println()
}
Output:

a=1 b=2 c=3

func (Stream[T]) All

func (s Stream[T]) All(predicate func(T) bool) bool

All returns true if all elements satisfy the predicate. Short-circuits on first non-match.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	fmt.Println(stream.Of(2, 4, 6).All(func(n int) bool { return n%2 == 0 }))
}
Output:

true

func (Stream[T]) Any

func (s Stream[T]) Any(predicate func(T) bool) bool

Any returns true if any element satisfies the predicate. Short-circuits on first match.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	fmt.Println(stream.Of(1, 2, 3).Any(func(n int) bool { return n > 2 }))
}
Output:

true

func (Stream[T]) Chain

func (s Stream[T]) Chain(others ...Stream[T]) Stream[T]

Chain concatenates multiple Streams, yielding all elements from each in order.

combined := s1.Chain(s2, s3)
Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	s1 := stream.Of(1, 2, 3)
	s2 := stream.Of(4, 5, 6)
	result := s1.Chain(s2).ToSlice()
	fmt.Println(result)
}
Output:

[1 2 3 4 5 6]

func (Stream[T]) Chunk

func (s Stream[T]) Chunk(size int) []Stream[T]

Chunk collects all elements and splits them into chunks of the specified size. Note: This operation consumes all elements into memory.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	chunks := stream.Of(1, 2, 3, 4, 5).Chunk(2)
	for _, c := range chunks {
		fmt.Println(c.ToSlice())
	}
}
Output:

[1 2]
[3 4]
[5]

func (Stream[T]) Contains

func (s Stream[T]) Contains(predicate func(T) bool) bool

Contains returns true if any element satisfies the predicate.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	has := stream.Of(1, 2, 3).Contains(func(n int) bool { return n == 2 })
	fmt.Println(has)
}
Output:

true

func (Stream[T]) Count

func (s Stream[T]) Count() int

Count returns the total number of elements. Warning: Consumes the entire Stream. Do not use on infinite sequences.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	n := stream.Of(1, 2, 3).Count()
	fmt.Println(n)
}
Output:

3

func (Stream[T]) CountBy

func (s Stream[T]) CountBy(predicate func(T) bool) int

CountBy returns the number of elements satisfying the predicate. Warning: Consumes the entire Stream. Do not use on infinite sequences.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	n := stream.Of(1, 2, 3, 4, 5).
		CountBy(func(v int) bool { return v%2 == 0 })
	fmt.Println(n)
}
Output:

2

func (Stream[T]) Distinct

func (s Stream[T]) Distinct(key func(T) string) Stream[T]

Distinct returns a Stream with duplicate elements removed. Uses the provided key function to determine equality. Note: Maintains a set of seen keys in memory.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of("a", "b", "a", "c", "b").
		Distinct(func(s string) string { return s }).
		ToSlice()
	fmt.Println(result)
}
Output:

[a b c]

func (Stream[T]) DropWhile

func (s Stream[T]) DropWhile(predicate func(T) bool) Stream[T]

DropWhile skips elements from the start while the predicate is true, then yields the rest.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(1, 2, 3, 4, 5).
		DropWhile(func(n int) bool { return n < 4 }).
		ToSlice()
	fmt.Println(result)
}
Output:

[4 5]

func (Stream[T]) Filter

func (s Stream[T]) Filter(predicate func(T) bool) Stream[T]

Filter returns a Stream that yields only elements satisfying the predicate.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(1, 2, 3, 4, 5, 6).
		Filter(func(n int) bool { return n%2 == 0 }).
		ToSlice()
	fmt.Println(result)
}
Output:

[2 4 6]

func (Stream[T]) Find

func (s Stream[T]) Find(predicate func(T) bool) (T, bool)

Find returns the first element matching the predicate. Short-circuits: stops iteration as soon as a match is found.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	v, ok := stream.Of(1, 2, 3, 4, 5).
		Find(func(n int) bool { return n > 3 })
	fmt.Println(v, ok)
}
Output:

4 true

func (Stream[T]) First

func (s Stream[T]) First() (T, bool)

First returns the first element and true, or zero value and false if empty. For infinite Streams, this returns immediately.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	v, ok := stream.Of(10, 20, 30).First()
	fmt.Println(v, ok)
}
Output:

10 true

func (Stream[T]) ForEach

func (s Stream[T]) ForEach(fn func(T))

ForEach executes a function for each element.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	stream.Of(1, 2, 3).ForEach(func(n int) {
		fmt.Printf("%d ", n)
	})
	fmt.Println()
}
Output:

1 2 3

func (Stream[T]) ForEachIndexed

func (s Stream[T]) ForEachIndexed(fn func(int, T))

ForEachIndexed executes a function for each element with its index.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	stream.Of("a", "b", "c").ForEachIndexed(func(i int, s string) {
		fmt.Printf("%d:%s ", i, s)
	})
	fmt.Println()
}
Output:

0:a 1:b 2:c

func (Stream[T]) IsEmpty

func (s Stream[T]) IsEmpty() bool

IsEmpty returns true if the Stream has no elements.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	fmt.Println(stream.Of[int]().IsEmpty())
	fmt.Println(stream.Of(1).IsEmpty())
}
Output:

true
false

func (Stream[T]) Last

func (s Stream[T]) Last() (T, bool)

Last returns the last element and true, or zero value and false if empty. Warning: This consumes the entire Stream. Do not use on infinite sequences.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	v, ok := stream.Of(10, 20, 30).Last()
	fmt.Println(v, ok)
}
Output:

30 true

func (Stream[T]) MaxBy

func (s Stream[T]) MaxBy(less func(a, b T) bool) (T, bool)

MaxBy returns the maximum element according to the comparison function. Warning: Consumes the entire Stream.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	v, _ := stream.Of(3, 1, 4, 1, 5).
		MaxBy(func(a, b int) bool { return a < b })
	fmt.Println(v)
}
Output:

5

func (Stream[T]) MinBy

func (s Stream[T]) MinBy(less func(a, b T) bool) (T, bool)

MinBy returns the minimum element according to the comparison function. Warning: Consumes the entire Stream.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	v, _ := stream.Of(3, 1, 4, 1, 5).
		MinBy(func(a, b int) bool { return a < b })
	fmt.Println(v)
}
Output:

1

func (Stream[T]) None

func (s Stream[T]) None(predicate func(T) bool) bool

None returns true if no elements satisfy the predicate.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	fmt.Println(stream.Of(1, 2, 3).None(func(n int) bool { return n > 10 }))
}
Output:

true

func (Stream[T]) Partition

func (s Stream[T]) Partition(predicate func(T) bool) (matched Stream[T], unmatched Stream[T])

Partition collects all elements and splits them into two Streams: elements that match the predicate and those that don't. Note: This operation consumes all elements into memory.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	evens, odds := stream.Of(1, 2, 3, 4, 5).
		Partition(func(n int) bool { return n%2 == 0 })
	fmt.Println("evens:", evens.ToSlice())
	fmt.Println("odds:", odds.ToSlice())
}
Output:

evens: [2 4]
odds: [1 3 5]

func (Stream[T]) Peek

func (s Stream[T]) Peek(fn func(T)) Stream[T]

Peek executes a side-effect function for each element without modifying the Stream. Useful for debugging or logging within a lazy chain.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	var log []string
	stream.Of("hello", "world").
		Peek(func(s string) { log = append(log, "saw:"+s) }).
		ToSlice()
	fmt.Println(log)
}
Output:

[saw:hello saw:world]

func (Stream[T]) Reduce

func (s Stream[T]) Reduce(initial T, fn func(acc, item T) T) T

Reduce folds all elements into a single value of the same type. For reducing to a different type, use the top-level Reduce function.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	sum := stream.Of(1, 2, 3, 4, 5).
		Reduce(0, func(acc, v int) int { return acc + v })
	fmt.Println(sum)
}
Output:

15

func (Stream[T]) Reject

func (s Stream[T]) Reject(predicate func(T) bool) Stream[T]

Reject returns a Stream that excludes elements satisfying the predicate.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(1, 2, 3, 4, 5).
		Reject(func(n int) bool { return n%2 == 0 }).
		ToSlice()
	fmt.Println(result)
}
Output:

[1 3 5]

func (Stream[T]) Reverse

func (s Stream[T]) Reverse() Stream[T]

Reverse buffers all elements and yields them in reverse order. Note: This operation consumes all elements into memory.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(1, 2, 3).Reverse().ToSlice()
	fmt.Println(result)
}
Output:

[3 2 1]

func (Stream[T]) Seq

func (s Stream[T]) Seq() iter.Seq[T]

Seq returns the underlying iter.Seq[T]. Use this for interop with standard library functions like slices.Collect.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	var result []int
	for v := range stream.Of(1, 2, 3).Seq() {
		result = append(result, v)
	}
	fmt.Println(result)
}
Output:

[1 2 3]

func (Stream[T]) Shuffle

func (s Stream[T]) Shuffle() Stream[T]

Shuffle buffers all elements, randomizes their order, and yields them. Note: This operation consumes all elements into memory.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	s := stream.Of(1, 2, 3, 4, 5).Shuffle()
	// Shuffle randomizes order, so just check count
	fmt.Println(s.Count())
}
Output:

5

func (Stream[T]) Skip

func (s Stream[T]) Skip(n int) Stream[T]

Skip returns a Stream that skips the first n elements.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(10, 20, 30, 40, 50).Skip(2).ToSlice()
	fmt.Println(result)
}
Output:

[30 40 50]

func (Stream[T]) Sort

func (s Stream[T]) Sort(cmp func(a, b T) int) Stream[T]

Sort buffers all elements, sorts them, and yields in sorted order. Note: This operation consumes all elements into memory, breaking pure laziness. However, subsequent operations in the chain remain lazy.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(3, 1, 4, 1, 5).
		Sort(func(a, b int) int { return a - b }).
		ToSlice()
	fmt.Println(result)
}
Output:

[1 1 3 4 5]

func (Stream[T]) Take

func (s Stream[T]) Take(n int) Stream[T]

Take returns a Stream that yields only the first n elements. For infinite sequences, this is the primary way to limit output.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(10, 20, 30, 40, 50).Take(3).ToSlice()
	fmt.Println(result)
}
Output:

[10 20 30]

func (Stream[T]) TakeLast

func (s Stream[T]) TakeLast(n int) Stream[T]

TakeLast buffers all elements and returns only the last n elements. Note: This operation consumes all elements into memory.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(10, 20, 30, 40, 50).TakeLast(2).ToSlice()
	fmt.Println(result)
}
Output:

[40 50]

func (Stream[T]) TakeWhile

func (s Stream[T]) TakeWhile(predicate func(T) bool) Stream[T]

TakeWhile returns elements from the start as long as the predicate is true.

Example
package main

import (
	"fmt"

	"github.com/nd-forge/stream"
)

func main() {
	result := stream.Of(1, 2, 3, 4, 5).
		TakeWhile(func(n int) bool { return n < 4 }).
		ToSlice()
	fmt.Println(result)
}
Output:

[1 2 3]

func (Stream[T]) ToSlice

func (s Stream[T]) ToSlice() []T

ToSlice collects all elements into a slice.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL