This package provides generic iterators and iterator transformations in go.
Iterators defer iteration execution by pushing iteration logic into the Next() function.
This package has a dependency on go-types for the Option[T any] type. Users of the iter module can utilize Result[T] to capture errors or perform transformations on slices that can contain errors.
Here are three ways to loop over an iterator
A for loop has init, condition and post sections that can be used on an iterator.
rng := iter.Range(0, 10)
for op := rng.Next(); op.IsSome(); op = rng.Next() {
// code in here
}
Async (calling range on a channel)
You can also loop over a channel created from an Iterator[T] using the iter.Async function. You can specify a context with the iter.WithContext[T] option. If no context is specified, context.Background() is used.
rng := iter.Range(0, 10)
for value := range iter.Async(rng) {
// code in here
}
Specify a context
rng := iter.Range(0,10)
op := iter.WithContext[int](context.TODO())
for value := range iter.Async(rng, op){
// code here
}
ForEach uses an anonymous function to iterate over each item to the caller.
rng := iter.Range(0, 10)
iter.ForEach(rng, func(i int) {
fmt.Print(" ")
fmt.Print(i)
})
// prints:
// 0 1 2 3 4 5 6 7 8 9
ForEachIndex uses an anonymous function to iterate over each item and the index of that item to the caller.
rng := iter.Range(0, 10)
iter.ForEachIndex(rng, func(index int, i int) {
if index > 0 {
fmt.Print(" ")
}
fmt.Print(i)
})
// prints:
// 0 1 2 3 4 5 6 7 8 9
The Range function creates an iterator over [begin..end)
(inclusive begin, exclusive end)
rng := iter.Range(0, 10)
iter.ForEachIndex(rng, func(index int, i int) {
if index > 0 {
fmt.Print(" ")
}
fmt.Print(i)
})
// prints:
// 0 1 2 3 4 5 6 7 8 9
The RangeWith function creates an iterator over the range begin..end
. The includeBegin and includeEnd flags
control if the range is inclusive start and inclusive end.
rng := iter.RangeWith(false, 0, 10, true)
iter.ForEachIndex(rng, func(index int, i int) {
if index > 0 {
fmt.Print(" ")
}
fmt.Print(i)
})
// prints:
// 1 2 3 4 5 6 7 8 9 10
Repeat returns an iterator that repeats the given element
, count
times
rep := iter.Repeat(10, 5)
iter.ForEachIndex(rng, func(index int, i int) {
if index > 0 {
fmt.Print(" ")
}
fmt.Print(i)
})
// prints:
// 10 10 10 10 10
The Select function transforms an iterator of one type to an iterator of another type.
rng := iter.Range(0,10)
strRng := iter.Select(rng, strconv.Itoa)
iter.ForEachIndex(strRng, func(index int, s string){
if index > 0{
fmt.Print(" ")
}
fmt.Print("'%s'", s)
})
// prints : '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
The Where function removes items from an iterator using a predicate function.
rng := iter.Range(0,10)
even := func(i int){ return i % 2 == 0}
evens := iter.Where(rng, even)
iter.ForEachIndex(evens, func(index int, i int){
if index > 0{
fmt.Print(" ")
}
fmt.Print("%d", i)
})
// prints : 0 2 4 6 8
The FromSlice function returns a slice iterator over the given slice
slice := []int{1, 3, 5, 7, 9}
it := iter.FromSlice(slice)
iter.ForEachIndex(it, func(index int, i int){
if index > 0{
fmt.Print(" ")
}
fmt.Print("%d", i)
})
// prints 1 3 5 7 9
The ToSlice function returns a slice from the given iterator by iterating over all elements.
rng := iter.Range(0, 10)
slice := iter.ToSlice(rng)
fmt.Println(slice)
// prints : [0 1 2 3 4 5 6 7 8 9]
The FromMap function returns an iterator over the given map. FromMap captures keys of the map in the first call to Next()
. Subsequent calls to Next()
track an index into the key slice to return the next key value pair. Each call to Next()
returns an Option tuple of Key Value pairs.
m := map[string]int{"0":0, "1":1, "2":2, "3":3}
it := iter.FromMap(m)
fmt.Print("[ ")
iter.ForEachIndex(it, func(index int, tup types.Tuple2[string, int]){
if index > 0{
fmt.Print(", ")
}
k, v := tup.Deconstruct()
fmt.Print("'%s':%d", k, v)
})
fmt.Print(" ]")
// prints : [ '0':0, '1':1, '2':2, '3':3 ]
The FromMapAsync function returns an iterator over the given map using channels and the supplied context. FromMapAsync uses a go routine to iterate over the map and channels to capture the key value pair. The Next()
function removes a tuple key, value from the channel.
m := map[string]int{"0":0, "1":1, "2":2, "3":3}
it := iter.FromMapAsync(m, context.Background())
fmt.Print("[ ")
iter.ForEachIndex(it, func(index int, tup types.Tuple2[string, int]){
if index > 0{
fmt.Print(", ")
}
k, v := tup.Deconstruct()
fmt.Print("'%s':%d", k, v)
})
fmt.Print(" ]")
// prints : [ '0':0, '1':1, '2':2, '3':3 ]
The Count function returns the count of element in the iterator by iterating over all the elements.
expected := 10
rng := iter.Range(0, expected)
actual := iter.Count(rng)
if actual != expected {
t.Fatalf("expected count of %d but found %d", expected, actual)
}
The FromChannel function returns an iterator over the given channel. A context can be specified in options for early termination.
ch := make(chan int)
go func(c chan int) {
defer close(c)
for i := 0; i < 10; i++ {
ch <- i
}
}(ch)
it := iter.FromChannel(ch)
iter.ForEach(it, func(i int) {
fmt.Println(i)
})
First returns the first item in the sequence. If the sequence has no more items, First returns types.None[T]
sequence := iter.Range(15, 30)
first := iter.First(sequence)
switch op := first.(type) {
case types.Some[int]:
fmt.Println(op.Value)
case types.None[int]:
fmt.Println("none")
}
// prints
// 15
FirstWhere returns the first item in the sequence that matches the condition. If the sequence has no more items or no matches exist, First returns types.None[T]
sequence := iter.Range(15, 30)
first := iter.FirstWhere(sequence, func(i int)bool{ return i % 10 == 0})
switch op := first.(type) {
case types.Some[int]:
fmt.Println(op.Value)
case types.None[int]:
fmt.Println("none")
}
// prints
// 20