seq

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2025 License: MIT Imports: 4 Imported by: 1

README

seq

GitHub Workflow Status go.dev reference GitHub go.mod Go version OpenSSF Scorecard

Utilities for working with Go 1.23+ iterator sequences.

Installation

go get github.com/sagikazarmark/seq

License

The project is licensed under the MIT License.

Documentation

Overview

Package seq provides utilities for working with Go 1.23+ iterator sequences.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Chain added in v0.1.0

func Chain[V any](seqs ...iter.Seq[V]) iter.Seq[V]

Chain takes two (or more) iterators and creates a new iterator over them in sequence.

The new iterator will iterate over values from each iterator in the order they are provided.

In other words, it links two iterators together, in a chain.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	users1 := slices.Values([]string{"alice", "bob"})
	users2 := slices.Values([]string{"charlie", "dave"})

	users := seq.Chain(users1, users2)

	for user := range users {
		fmt.Println(user)
	}

}
Output:

alice
bob
charlie
dave

func Chain2 added in v0.1.0

func Chain2[K any, V any](seqs ...iter.Seq2[K, V]) iter.Seq2[K, V]

Chain2 takes two (or more) iterators and creates a new iterator over them in sequence.

The new iterator will iterate over pairs from each iterator in the order they are provided.

In other words, it links two iterators together, in a chain.

Example
package main

import (
	"fmt"
	"iter"
	"maps"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	users1 := maps.All(map[string]string{"alice": "admin", "bob": "admin"})
	users2 := maps.All(map[string]string{"bob": "user", "charlie": "manager"})

	users := seq.Chain2(users1, users2)

	printSorted(users)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

alice: admin
bob: admin
bob: user
charlie: manager

func Filter

func Filter[V any](seq iter.Seq[V], predicate func(V) bool) iter.Seq[V]

Filter creates an iterator using a predicate to determine if a value should be yielded.

The returned iterator will yield only the values for which the predicate is true.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	numbers := slices.Values([]int{1, 2, 3, 4, 5})

	oddFilter := func(n int) bool {
		return n%2 == 1
	}

	oddNumbers := seq.Filter(numbers, oddFilter)

	for n := range oddNumbers {
		fmt.Println(n)
	}

}
Output:

1
3
5

func Filter2

func Filter2[K any, V any](seq iter.Seq2[K, V], predicate func(K, V) bool) iter.Seq2[K, V]

Filter2 creates an iterator using a predicate to determine if a pair should be yielded.

The returned iterator will yield only the pairs for which the predicate is true.

Example
package main

import (
	"fmt"
	"iter"
	"maps"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	users := maps.All(map[string]string{"alice": "admin", "bob": "user", "charlie": "manager", "dave": "manager"})

	managerFilter := func(k string, v string) bool {
		return len(k) > 3 && v == "manager"
	}

	managers := seq.Filter2(users, managerFilter)

	printSorted(managers)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

charlie: manager
dave: manager

func FilterMap added in v0.1.0

func FilterMap[V any, U any](seq iter.Seq[V], fn func(V) (U, bool)) iter.Seq[U]

FilterMap creates an iterator that both filters and maps.

See Filter and Map for details.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	numbers := slices.Values([]int{1, 2, 3, 4, 5, 6})

	// Filter odd numbers and double them
	doubleIfOdd := func(n int) (int, bool) {
		return n * 2, n%2 == 1
	}

	doubledOdd := seq.FilterMap(numbers, doubleIfOdd)

	for n := range doubledOdd {
		fmt.Println(n)
	}

}
Output:

2
6
10

func FilterMap2 added in v0.1.0

func FilterMap2[K any, V any, U any](seq iter.Seq2[K, V], fn func(K, V) (U, bool)) iter.Seq2[K, U]

FilterMap2 creates an iterator that both filters and maps.

See Filter2 and Map2 for details.

Example
package main

import (
	"fmt"
	"iter"
	"maps"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	salaries := maps.All(map[string]float64{"alice": 100000, "bob": 80000, "charlie": 120000, "dave": 50000})

	// Give everyone with a salary under 100,000 credits a raise of 5%
	// and produce a list of names and new salaries
	raise := func(k string, v float64) (float64, bool) {
		if v < 100000 {
			return v * 1.05, true
		}

		return v, false
	}

	promotions := seq.FilterMap2(salaries, raise)

	printSorted(promotions)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

bob: 84000
dave: 52500

func Flatten

func Flatten[V any](seq iter.Seq[[]V]) iter.Seq[V]

Flatten returns an iterator that yields all elements of each slice produced by the outer sequence. It takes a sequence of slices and "flattens" it into a sequence of individual elements.

The elements are yielded in order: first all elements from the first slice, then all elements from the second slice, and so on.

This is useful for processing nested data structures or combining results from multiple operations that return slices.

Example
package main

import (
	"fmt"
	"iter"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	job1 := func() []string {
		return []string{"foo", "bar"}
	}
	job2 := func() []string {
		return []string{"baz"}
	}
	job3 := func() []string {
		return []string{"bat", "qux", "quux"}
	}

	doJobs := func() iter.Seq[[]string] {
		return slices.Values([][]string{job1(), job2(), job3()})
	}

	results := seq.Flatten(doJobs())

	for result := range results {
		fmt.Println(result)
	}

}
Output:

foo
bar
baz
bat
qux
quux

func Map added in v0.1.0

func Map[V any, U any](seq iter.Seq[V], fn func(V) U) iter.Seq[U]

Map creates an iterator that transforms values using a function.

The returned iterator will yield the transformed values.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	numbers := slices.Values([]int{1, 2, 3, 4, 5})

	double := func(n int) int {
		return n * 2
	}

	doubled := seq.Map(numbers, double)

	for n := range doubled {
		fmt.Println(n)
	}

}
Output:

2
4
6
8
10

func Map2 added in v0.1.0

func Map2[K any, V any, U any](seq iter.Seq2[K, V], fn func(K, V) U) iter.Seq2[K, U]

Map2 creates an iterator that transforms values of a pair using a function.

The returned iterator will yield the transformed values.

Example
package main

import (
	"fmt"
	"iter"
	"maps"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	users := maps.All(map[string]string{"alice": "admin", "bob": "user", "charlie": "manager", "dave": "manager"})
	payGrades := map[string]int{"admin": 1000, "user": 500, "manager": 1500}

	// Calculate salaries for each user based on their role
	calculateSalary := func(k string, v string) int {
		return payGrades[v]
	}

	salaries := seq.Map2(users, calculateSalary)

	printSorted(salaries)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

alice: 1000
bob: 500
charlie: 1500
dave: 1500

func Repeat added in v0.1.0

func Repeat[V any](v V) iter.Seq[V]

Repeat creates an iterator that yields the same value over and over.

WARNING: This iterator will never terminate on its own.

Example
package main

import (
	"fmt"

	"github.com/sagikazarmark/seq"
)

func main() {
	meaningOfLife := seq.Repeat(42)

	var i int

	for n := range meaningOfLife {
		fmt.Println(n)
		i++
		if i == 5 {
			break
		}
	}

}
Output:

42
42
42
42
42

func Skip added in v0.1.0

func Skip[V comparable](seq iter.Seq[V], n uint) iter.Seq[V]

Skip creates an iterator that skips values until n values are skipped or the end of the iterator is reached (whichever happens first).

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	fruits := slices.Values([]string{"apple", "banana", "cherry", "grape", "mango"})

	skip3 := seq.Skip(fruits, 3)

	for n := range skip3 {
		fmt.Println(n)
	}

}
Output:

grape
mango

func Skip2 added in v0.1.0

func Skip2[K comparable, V any](seq iter.Seq2[K, V], n uint) iter.Seq2[K, V]

Skip2 creates an iterator that skips pairs until n pairs are skipped or the end of the iterator is reached (whichever happens first).

Example
package main

import (
	"fmt"
	"iter"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	users := seq.Sorted2(map[string]string{"alice": "admin", "bob": "user", "charlie": "manager", "dave": "manager"})

	skip2 := seq.Skip2(users, 2)

	printSorted(skip2)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

charlie: manager
dave: manager

func SkipWhile added in v0.1.0

func SkipWhile[V comparable](seq iter.Seq[V], predicate func(V) bool) iter.Seq[V]

SkipWhile creates an iterator that yields values based on a predicate.

It will evaluate the predicate on each value, and skip values while it evaluates true. After false is returned, the rest of the values are yielded.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	fruits := slices.Values([]string{"apple", "apricot", "acerola", "banana", "cherry", "grape", "mango"})

	noAs := seq.SkipWhile(fruits, func(v string) bool { return v[0] == 'a' })

	for n := range noAs {
		fmt.Println(n)
	}

}
Output:

banana
cherry
grape
mango

func SkipWhile2 added in v0.1.0

func SkipWhile2[K comparable, V any](seq iter.Seq2[K, V], predicate func(K, V) bool) iter.Seq2[K, V]

SkipWhile2 creates an iterator that yields pairs based on a predicate.

It will evaluate the predicate on each pair, and skip pairs while it evaluates true. After false is returned, the rest of the pairs are yielded.

Example
package main

import (
	"fmt"
	"iter"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	users := seq.Sorted2(map[string]string{"alice": "admin", "bob": "admin", "charlie": "manager", "dave": "user"})

	skipAdmins := func(k string, v string) bool {
		return v == "admin"
	}

	afterAdmins := seq.SkipWhile2(users, skipAdmins)

	printSorted(afterAdmins)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

charlie: manager
dave: user

func Sorted2 added in v0.1.0

func Sorted2[K cmp.Ordered, V any](m map[K]V) iter.Seq2[K, V]

Sorted2 creates an iterator that yields pairs in a sorted order (by key).

Useful when you need to iterate over a map in a sorted order.

Example
package main

import (
	"fmt"

	"github.com/sagikazarmark/seq"
)

func main() {
	users := seq.Sorted2(map[string]string{"charlie": "manager", "bob": "user", "alice": "admin", "dave": "manager"})

	for user, role := range users {
		fmt.Printf("%s: %s\n", user, role)
	}

}
Output:

alice: admin
bob: user
charlie: manager
dave: manager

func Take added in v0.1.0

func Take[V comparable](seq iter.Seq[V], n uint) iter.Seq[V]

Take creates an iterator that yields the first n values, or fewer if the underlying iterator ends sooner.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	fruits := slices.Values([]string{"apple", "banana", "cherry", "grape", "mango"})

	first3 := seq.Take(fruits, 3)

	for n := range first3 {
		fmt.Println(n)
	}

}
Output:

apple
banana
cherry

func Take2 added in v0.1.0

func Take2[K comparable, V any](seq iter.Seq2[K, V], n uint) iter.Seq2[K, V]

Take2 creates an iterator that yields the first n pairs, or fewer if the underlying iterator ends sooner.

Example
package main

import (
	"fmt"
	"iter"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	users := seq.Sorted2(map[string]string{"alice": "admin", "bob": "user", "charlie": "manager", "dave": "manager"})

	first3 := seq.Take2(users, 3)

	printSorted(first3)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

alice: admin
bob: user
charlie: manager

func TakeWhile added in v0.1.0

func TakeWhile[V comparable](seq iter.Seq[V], predicate func(V) bool) iter.Seq[V]

TakeWhile creates an iterator that yields values based on a predicate.

It will evaluate the predicate on each value, and yield values while it evaluates true. After false is returned, the rest of the values are ignored.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	fruits := slices.Values([]string{"apple", "apricot", "acerola", "banana", "cherry", "grape", "mango"})

	startsWithA := seq.TakeWhile(fruits, func(v string) bool { return v[0] == 'a' })

	for n := range startsWithA {
		fmt.Println(n)
	}

}
Output:

apple
apricot
acerola

func TakeWhile2 added in v0.1.0

func TakeWhile2[K comparable, V any](seq iter.Seq2[K, V], predicate func(K, V) bool) iter.Seq2[K, V]

TakeWhile2 creates an iterator that yields pairs based on a predicate.

It will evaluate the predicate on each pair, and yield pairs while it evaluates true. After false is returned, the rest of the pairs are ignored.

Example
package main

import (
	"fmt"
	"iter"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	users := seq.Sorted2(map[string]string{"alice": "admin", "bob": "admin", "charlie": "manager", "dave": "manager"})

	firstAdmins := seq.TakeWhile2(users, func(k string, v string) bool { return v == "admin" })

	printSorted(firstAdmins)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

alice: admin
bob: admin

func Uniq

func Uniq[V comparable](seq iter.Seq[V]) iter.Seq[V]

Uniq ensures only unique values are returned from a sequence.

Items are returned in the order they first appear.

Example
package main

import (
	"fmt"
	"slices"

	"github.com/sagikazarmark/seq"
)

func main() {
	numbers := slices.Values([]int{1, 2, 2, 3, 1, 4, 3, 5})

	unique := seq.Uniq(numbers)

	for number := range unique {
		fmt.Println(number)
	}

}
Output:

1
2
3
4
5

func Uniq2

func Uniq2[K comparable, V any](seq iter.Seq2[K, V]) iter.Seq2[K, V]

Uniq2 ensures only unique keys are returned from a sequence.

Note: You can achieve a similar effect using maps.Collect, but that requires loading the entire sequence into memory. Additionally, while maps.Collect returns the last seen value for each key, Uniq2 returns the first seen value for each key.

Example
package main

import (
	"fmt"
	"iter"
	"maps"
	"sort"
	"strings"

	"github.com/sagikazarmark/seq"
)

func main() {
	roles := seq.Chain2(
		maps.All(map[string]string{"alice": "admin", "bob": "user"}),
		maps.All(map[string]string{"alice": "user", "charlie": "user"}),
	)

	unique := seq.Uniq2(roles)

	printSorted(unique)

}

// Maps are unordered, so we need to hack for examples
func printSorted[K any, V any](s iter.Seq2[K, V]) {
	var output []string

	for key, value := range s {
		output = append(output, fmt.Sprintf("%v: %v\n", key, value))
	}

	sort.Strings(output)
	fmt.Print(strings.Join(output, ""))
}
Output:

alice: admin
bob: user
charlie: user

func ValuesErr

func ValuesErr[Slice ~[]E, E any](s Slice, err error) (iter.Seq[E], error)

ValuesErr returns an iterator that yields the slice elements in order, or an error if one was provided.

If err is not nil, ValuesErr returns (nil, err). If err is nil, it returns an iterator over the slice values.

This is useful for chaining operations without intermediate variables:

items, err := seq.ValuesErr(fetchItems())
if err != nil {
	return err
}

for item := range items {
	// process item
}

Instead of:

itemSlice, err := fetchItems()
if err != nil {
	return err
}

items := slices.Values(itemSlice)
for item := range items {
	// process item
}
Example (Error)
package main

import (
	"errors"
	"fmt"

	"github.com/sagikazarmark/seq"
)

func main() {
	fn := func() ([]struct{}, error) {
		return nil, errors.New("something went wrong")
	}

	_, err := seq.ValuesErr(fn())
	if err != nil {
		fmt.Println(err)
	}

}
Output:

something went wrong
Example (Ok)
package main

import (
	"fmt"

	"github.com/sagikazarmark/seq"
)

func main() {
	fn := func() ([]string, error) {
		return []string{"apple", "banana", "cherry"}, nil
	}

	fruits, err := seq.ValuesErr(fn())
	if err != nil {
		panic(err)
	}

	for fruit := range fruits {
		fmt.Println(fruit)
	}

}
Output:

apple
banana
cherry

Types

This section is empty.

Jump to

Keyboard shortcuts

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