itertools

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 1, 2024 License: Apache-2.0 Imports: 4 Imported by: 0

README

itertools GitHub Workflow Status (with event) Go Report Card Codecov Go Reference

itertools is a Go library introducing Iterator type to iterate over elements of collections/sequences with many methods similar to Rust or Python iterators.

Documentation

Documentation for the library packages and examples are available with GoDoc.

Installation

go get github.com/KSpaceer/itertools@latest

Example

data := []int{8, 1, 2, 3, 8, 7, 4, 5, 1, 6, 7, 8}

// finding max value in data
maxValue := itertools.Max(itertools.NewSliceIterator(data))

// transform number so it is in range [0, 1]
normalizeFunc := func(n int) float64 {
    return float64(n) / float64(maxValue)
}

// iterator for normalized values
normalizedIter := itertools.Map(
    itertools.NewSliceIterator(data),
    normalizeFunc,
)

// iterator for original slice
originalIter := itertools.NewSliceIterator(data)

// zipping normalized iterator with the original one to output values together
iter := itertools.Zip(originalIter, normalizedIter)

for iter.Next() {
    value, normalizedValue := iter.Elem().Unpack()
    fmt.Printf("original value: %d\tnormalized value: %.2f\n", value, normalizedValue)
}

Run with Playground.

See more examples in docs.

Documentation

Overview

Package itertools provides Iterator type to iteratively access elements of collections/sequences and many functions and methods to works with iterators.

Example (MapFilter)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"strings"
	"time"
)

// imitating account name or something
type Account string

// stub representation of financial transaction
type Transaction struct {
	From      Account
	To        Account
	Amount    int
	Timestamp time.Time
}

// stub representation of transaction message from message queue
type Message struct {
	FromTo string
	Amount int
	Time   string
}

func messageToTransaction(msg Message) Transaction {
	var tx Transaction
	parts := strings.SplitN(msg.FromTo, "---", 2)
	tx.From, tx.To = Account(parts[0]), Account(parts[1])
	tx.Amount = msg.Amount
	tx.Timestamp, _ = time.Parse(time.RFC3339Nano, msg.Time)
	return tx
}

// mock fraud checker for financial transactions
type FraudChecker struct {
	definitelyFraudulent Account
}

func (fc *FraudChecker) IsFraudulentTransaction(tx Transaction) bool {
	return tx.To == fc.definitelyFraudulent || tx.From == fc.definitelyFraudulent
}

// mock fraud transaction alerter
type Alerter struct{}

func (Alerter) Alert(tx Transaction) {
	var sb strings.Builder
	sb.WriteString("!!!FOUND FRAUD TRANSACTION!!!\n")
	sb.WriteString(fmt.Sprintf("From: %q\n", tx.From))
	sb.WriteString(fmt.Sprintf("To: %q\n", tx.To))
	sb.WriteString(fmt.Sprintf("Amount: %d\n", tx.Amount))
	fmt.Println(sb.String())
}

// mock consumer of message queue/broker
type MessageConsumer []Message

// imitating process of message consumption
func (mc *MessageConsumer) StartConsume() <-chan Message {
	messages := make([]Message, len(*mc))
	copy(messages, *mc)

	ch := make(chan Message)

	go func() {
		for _, msg := range messages {
			ch <- msg
		}
		close(ch)
	}()
	return ch
}

func main() {
	const fraud = "1"

	consumer := MessageConsumer{
		{
			FromTo: "2---3",
			Amount: 15,
			Time:   time.Now().Format(time.RFC3339Nano),
		},
		{
			FromTo: "3---1",
			Amount: 15,
			Time:   time.Now().Format(time.RFC3339Nano),
		},
		{
			FromTo: "5---6",
			Amount: 13,
			Time:   time.Now().Format(time.RFC3339Nano),
		},
		{
			FromTo: "7---8",
			Amount: 5,
			Time:   time.Now().Format(time.RFC3339Nano),
		},
		{
			FromTo: "4---1",
			Amount: 10,
			Time:   time.Now().Format(time.RFC3339Nano),
		},
		{
			FromTo: "1---0",
			Amount: 25,
			Time:   time.Now().Format(time.RFC3339Nano),
		},
	}

	ch := consumer.StartConsume()

	fraudChecker := FraudChecker{definitelyFraudulent: fraud}

	alerter := Alerter{}

	iter := itertools.Map(
		// iterating over messages
		itertools.NewChanIterator(ch),
		// mapping them to transactions
		messageToTransaction,
		// keeping only fraudulent ones
	).Filter(fraudChecker.IsFraudulentTransaction)

	iter.Range(func(tx Transaction) bool {
		alerter.Alert(tx)
		return true
	})
}
Output:

!!!FOUND FRAUD TRANSACTION!!!
From: "3"
To: "1"
Amount: 15

!!!FOUND FRAUD TRANSACTION!!!
From: "4"
To: "1"
Amount: 10

!!!FOUND FRAUD TRANSACTION!!!
From: "1"
To: "0"
Amount: 25
Example (MapReduce)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"math"
)

func main() {
	data := []int{6, 10, 7, 12, 6, 14, 8, 13, 10, 14}

	sum := itertools.Sum(itertools.NewSliceIterator(data))
	avg := float64(sum) / float64(len(data))

	stddev := itertools.
		Map(
			// iterating over data
			itertools.NewSliceIterator(data),
			// transforming data from int to float64
			func(n int) float64 { return float64(n) },
		).
		// calculating standard deviation of data
		Reduce(0, func(acc float64, elem float64) float64 {
			return acc + (elem-avg)*(elem-avg)
		})

	stddev = math.Sqrt(stddev / float64(len(data)-1))
	fmt.Println("data:", data)
	fmt.Printf("standard deviation: %.2f", stddev)
}
Output:

data: [6 10 7 12 6 14 8 13 10 14]
standard deviation: 3.16
Example (Set)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"sync"
	"time"
)

func main() {
	values := map[int]struct{}{}
	for i := 0; i < 1_000_000; i++ {
		values[i%40000] = struct{}{}
	}

	const batchSize = 2500

	iter := itertools.
		// batching incoming values into batches with size batchSize (2500)
		Batched(itertools.NewMapKeysIterator(values).
			// keeping only numbers divisible by 4
			Filter(func(n int) bool { return n%4 == 0 }),
			batchSize)

	var wg sync.WaitGroup

	for iter.Next() {
		batch := iter.Elem()
		wg.Add(1)
		go func() {
			defer wg.Done()
			process(batch)
		}()
	}

	wg.Wait()
}

func process(values []int) {
	// long processing imitation
	time.Sleep(time.Duration(len(values)) * time.Microsecond)
	fmt.Printf("processed %d items\n", len(values))
}
Output:

processed 2500 items
processed 2500 items
processed 2500 items
processed 2500 items

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Find

func Find[T any](i *Iterator[T], f func(T) bool) (T, bool)

Find applies function f to elements of iterator, returning first element for which the function returned true. The returned boolean value shows if the element was found (i.e. is valid). If no element was found, Find returns false as second returned value.

Example (Found)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	type Person struct {
		Name string
		Age  uint
	}

	people := []Person{
		{"Bob", 31},
		{"John", 42},
		{"Michael", 17},
		{"Jenny", 26},
	}

	target := "Michael"

	iter := itertools.NewSliceIterator(people)

	person, found := itertools.Find(iter, func(person Person) bool {
		return person.Name == target
	})

	if found {
		fmt.Printf("Found person with name %q. Age: %d\n", person.Name, person.Age)
	} else {
		fmt.Printf("Failed to find person with name %q\n", target)
	}
}
Output:

Found person with name "Michael". Age: 17
Example (Not_found)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	type Person struct {
		Name string
		Age  uint
	}

	people := []Person{
		{"Bob", 31},
		{"John", 42},
		{"Michael", 17},
		{"Jenny", 26},
	}

	target := "Mike"

	iter := itertools.NewSliceIterator(people)

	person, found := itertools.Find(iter, func(person Person) bool {
		return person.Name == target
	})

	if found {
		fmt.Printf("Found person with name %q. Age: %d\n", person.Name, person.Age)
	} else {
		fmt.Printf("Failed to find person with name %q\n", target)
	}
}
Output:

Failed to find person with name "Mike"

func Max

func Max[T cmp.Ordered](i *Iterator[T]) T

Max return max value of iterator.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{-80, 23, 0, 54, 13, -39, 45, 33}

	iter := itertools.NewSliceIterator(s)

	fmt.Println("Max value:", itertools.Max(iter))
}
Output:

Max value: 54

func Min

func Min[T cmp.Ordered](i *Iterator[T]) T

Min return min value of iterator.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{-80, 23, 0, 54, 13, -39, 45, 33}

	iter := itertools.NewSliceIterator(s)

	fmt.Println("Min value:", itertools.Min(iter))
}
Output:

Min value: -80

func Sum

func Sum[T Summable](i *Iterator[T]) T

Sum returns sum of iterator elements

Types

type AllocationOption

type AllocationOption func(options *allocOptions)

AllocationOption allows to manipulate allocations in iteration methods/functions.

func WithPrealloc

func WithPrealloc(prealloc int) AllocationOption

WithPrealloc sets preallocation size (capacity) for allocated buffers/slices.

type Enumeration

type Enumeration[T any] Pair[T, int]

Enumeration is a specific case of Pair for Enumerate function.

func (Enumeration[T]) Unpack

func (p Enumeration[T]) Unpack() (T, int)

Unpack returns values of Enumeration as tuple.

type Iterator

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

Iterator is used to process all elements of some collection or sequence. Iterator contains methods to access the elements and to process or aggregate them in many ways.

func Batched

func Batched[T any](i *Iterator[T], batchSize int) *Iterator[[]T]

Batched creates new iterator that returns slices of T (aka batch) with size up to batchSize, using given source iterator.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}

	iter := itertools.Batched(itertools.NewSliceIterator(s), 2)

	for iter.Next() {
		fmt.Println("got batch:", iter.Elem())
	}
}
Output:

got batch: [1 2]
got batch: [3 4]
got batch: [5 6]
got batch: [7 8]
got batch: [9]

func Chain

func Chain[T any](iters ...*Iterator[T]) *Iterator[T]

Chain chains iterators, returning resulting chained iterator. The result iterator yields elements of the first iterator, then elements of the second one etc.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	iter1 := itertools.NewSliceIterator([]byte("Hello"))

	ch := make(chan byte)
	go func() {
		ch <- ' '
		close(ch)
	}()
	iter2 := itertools.NewChanIterator(ch)
	iter3 := itertools.NewAsciiIterator("World!")

	chainedIter := itertools.Chain(iter1, iter2, iter3)

	fmt.Println("chained collected result:", string(chainedIter.Collect()))
}
Output:

chained collected result: Hello World!

func Cycle

func Cycle[T any](i *Iterator[T], opts ...AllocationOption) *Iterator[T]

Cycle creates new iterator that endlessly repeats elements of source iterator. If source iterator is empty, the cycle iterator is also empty.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{1, 2, 3}

	iter := itertools.Cycle(itertools.NewSliceIterator(s))

	for i := 0; i < 10; i++ {
		iter.Next()
		fmt.Println(iter.Elem())
	}
}
Output:

1
2
3
1
2
3
1
2
3
1

func Enumerate

func Enumerate[T any](i *Iterator[T]) *Iterator[Enumeration[T]]

Enumerate creates new iterator that returns Enumeration contating current element of source iterator along with current iteration count (starting from 0).

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	type Person struct {
		Name string
		Age  uint
	}

	people := []Person{
		{"Bob", 31},
		{"John", 42},
		{"Michael", 17},
		{"Jenny", 26},
	}

	iter := itertools.Enumerate(itertools.NewSliceIterator(people))

	for iter.Next() {
		person, i := iter.Elem().Unpack()
		fmt.Printf("Index: %d ||| Name: %s ||| Age: %d\n", i, person.Name, person.Age)
	}
}
Output:

Index: 0 ||| Name: Bob ||| Age: 31
Index: 1 ||| Name: John ||| Age: 42
Index: 2 ||| Name: Michael ||| Age: 17
Index: 3 ||| Name: Jenny ||| Age: 26

func Map

func Map[T, U any](i *Iterator[T], mapper func(T) U) *Iterator[U]

Map returns new iterator that yields elements of type U by calling mapper to each element of type T of source iterator.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"math"
)

func main() {
	const maxPoints = 150
	results := []int{25, 100, 95, 36, 145, 67, 49, 123}

	percentageIter := itertools.Map(
		itertools.NewSliceIterator(results),
		func(result int) int {
			return int(math.Round(float64(result) / maxPoints * 100))
		},
	)

	fmt.Println("Results percentage:", percentageIter.Collect())
}
Output:

Results percentage: [17 67 63 24 97 45 33 82]

func New

func New[T any](f func() (T, bool)) *Iterator[T]

New creates new Iterator using given iteration function. Function returns an element of collection and boolean value indicating if the element is valid (i.e. false means that the iteration is over).

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	arr := [5]int{5, 10, 15, 20, 25}
	var idx int

	// function to manually iterator over slice.
	// It is more comfortable to use itertools.NewSliceIterator in this case
	f := func() (int, bool) {
		if idx >= len(arr) {
			return 0, false
		}
		elem := arr[idx]
		idx++
		return elem, true
	}

	iter := itertools.New(f)

	fmt.Println(iter.Next(), iter.Elem())
	fmt.Println(iter.Next(), iter.Elem())
	fmt.Println(iter.Next(), iter.Elem())
	fmt.Println(iter.Next(), iter.Elem())
	fmt.Println(iter.Next(), iter.Elem())
	fmt.Println(iter.Next())
}
Output:

true 5
true 10
true 15
true 20
true 25
false

func NewAsciiIterator

func NewAsciiIterator(s string) *Iterator[byte]

NewAsciiIterator creates iterator yielding bytes from string (interpreting string as []byte).

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"unicode"
)

func main() {
	s := "Hello, World!"

	iter := itertools.NewAsciiIterator(s)

	iter = iter.Filter(func(b byte) bool {
		return unicode.IsPunct(rune(b))
	})

	fmt.Println("phrase punctuation signs:", string(iter.Collect()))
}
Output:

phrase punctuation signs: ,!

func NewChanIterator

func NewChanIterator[T any](ch <-chan T) *Iterator[T]

NewChanIterator creates iterator yielding values from channel until the channel is closed.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"slices"
	"sync"
)

func main() {
	ch := make(chan int)
	var wg sync.WaitGroup
	for i := 0; i < 3; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < 5; j++ {
				ch <- j
			}
		}()
	}

	go func() {
		wg.Wait()
		close(ch)
	}()

	iter := itertools.NewChanIterator(ch)
	result := iter.Collect()
	slices.Sort(result)
	fmt.Println(result)
}
Output:

[0 0 0 1 1 1 2 2 2 3 3 3 4 4 4]

func NewMapIterator

func NewMapIterator[K comparable, V any](m map[K]V) *Iterator[Pair[K, V]]

NewMapIterator creates iterator yielding key-value pairs from map. NewMapIterator uses reflect package to keep iteration state.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"slices"
)

func main() {
	months := map[int]string{
		1:  "January",
		2:  "February",
		3:  "March",
		4:  "April",
		5:  "May",
		6:  "June",
		7:  "July",
		8:  "August",
		9:  "September",
		10: "October",
		11: "November",
		12: "December",
	}

	iter := itertools.NewMapIterator(months)

	summerMonths := []string{"June", "July", "August"}

	iter = iter.Filter(func(p itertools.Pair[int, string]) bool {
		return slices.Contains(summerMonths, p.Second)
	})

	// iterating over map keys (months numbers) rather than entire key-value pairs
	numbersIter := itertools.Map(iter, func(p itertools.Pair[int, string]) int {
		return p.First
	})

	summerMonthsNumbers := numbersIter.Collect()
	slices.Sort(summerMonthsNumbers)
	fmt.Println("summer months numbers:", summerMonthsNumbers)
}
Output:

summer months numbers: [6 7 8]

func NewMapKeysIterator

func NewMapKeysIterator[K comparable, V any](m map[K]V) *Iterator[K]

NewMapKeysIterator creates iterator yielding keys from map. NewMapKeysIterator uses reflect package to keep iteration state.

func NewMapValuesIterator

func NewMapValuesIterator[K comparable, V any](m map[K]V) *Iterator[V]

NewMapValuesIterator creates iterator yielding values from map. NewMapValuesIterator uses reflect package to keep iteration state.

func NewSliceIterator

func NewSliceIterator[S ~[]T, T any](s S) *Iterator[T]

NewSliceIterator creates iterator for given slice, meaning iterator will yield all elements of the slice.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{1, 2, 3, 4, 5}

	iter := itertools.NewSliceIterator(s)

	for iter.Next() {
		fmt.Println(iter.Elem())
	}
	fmt.Println("is finished:", !iter.Next())
}
Output:

1
2
3
4
5
is finished: true

func NewUTF8Iterator

func NewUTF8Iterator(s string) *Iterator[rune]

NewUTF8Iterator creates iterator yielding runes from string (interpreting string as []rune)

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
	"unicode"
)

func main() {
	s := "Hello, 世界"

	iter := itertools.NewUTF8Iterator(s)

	iter = iter.Filter(func(r rune) bool {
		return unicode.Is(unicode.Han, r)
	})

	fmt.Println("chinese hieroglyphs:", string(iter.Collect()))
}
Output:

chinese hieroglyphs: 世界

func Repeat

func Repeat[T any](elem T) *Iterator[T]

Repeat creates new iterator that endlessly yields elem.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	iter := itertools.Repeat("HELLO")

	for i := 0; i < 5; i++ {
		iter.Next()
		fmt.Println(iter.Elem())
	}
}
Output:

HELLO
HELLO
HELLO
HELLO
HELLO

func Zip

func Zip[T, U any](t *Iterator[T], u *Iterator[U]) *Iterator[Pair[T, U]]

Zip joins two iterators into a one yielding Pair of the iterators' elements. Returned iterator yields Pairs until one of source iterators is empty.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	names := []string{"Bob", "John", "Michael", "Jenny"}
	ages := []uint{31, 42, 17, 26}

	nameIter := itertools.NewSliceIterator(names)
	ageIter := itertools.NewSliceIterator(ages)

	iter := itertools.Zip(nameIter, ageIter)

	for iter.Next() {
		name, age := iter.Elem().Unpack()
		fmt.Printf("Name: %s ::: Age: %d\n", name, age)
	}
}
Output:

Name: Bob ::: Age: 31
Name: John ::: Age: 42
Name: Michael ::: Age: 17
Name: Jenny ::: Age: 26

func (*Iterator[T]) All

func (i *Iterator[T]) All(f func(T) bool) bool

All applies function f to every element of iterator. If f returns true for all elements of iterator, All returns true. Otherwise, All returns false. All is lazy and will stop iterating after first element for which f returns false. All returns true for empty iterator.

Example (False)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{-4, -3, -2, -1, 0, 1, 2, 3, 4}

	predicate := func(n int) bool {
		return n < 2
	}

	iter := itertools.NewSliceIterator(s)

	fmt.Println(iter.All(predicate))
}
Output:

false
Example (True)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{-4, -3, -2, -1, 0, 1, 2, 3, 4}

	predicate := func(n int) bool {
		if n < 0 {
			n = -n
		}
		return n < 5
	}

	iter := itertools.NewSliceIterator(s)

	fmt.Println(iter.All(predicate))
}
Output:

true

func (*Iterator[T]) Any

func (i *Iterator[T]) Any(f func(T) bool) bool

Any applies function f to every element of iterator. If f returns false for all elements of iterator, Any returns false. Otherwise, Any return true. Any is lazy and will stop iterating after first element for which f returns true. Any returns false for empty iterator.

Example (False)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{-4, -3, -2, -1, 0, 1, 2, 3, 4}

	predicate := func(n int) bool {
		if n < 0 {
			n = -n
		}
		return n >= 5
	}

	iter := itertools.NewSliceIterator(s)

	fmt.Println(iter.Any(predicate))
}
Output:

false
Example (True)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{-4, -3, -2, -1, 0, 1, 2, 3, 4}

	predicate := func(n int) bool {
		return n < 2
	}

	iter := itertools.NewSliceIterator(s)

	fmt.Println(iter.Any(predicate))
}
Output:

true

func (*Iterator[T]) Collect

func (i *Iterator[T]) Collect(opts ...AllocationOption) []T

Collect returns all elements of iterator as slice.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []string{"First", "Second", "Third"}
	idx := len(s) - 1

	// function to iterate over slice in reverse order
	f := func() (string, bool) {
		if idx < 0 {
			return "", false
		}
		elem := s[idx]
		idx--
		return elem, true
	}

	iter := itertools.New(f)

	fmt.Println(iter.Collect())
}
Output:

[Third Second First]

func (*Iterator[T]) Count

func (i *Iterator[T]) Count() int

Count returns amount of remaining elements in iterator. Call of Count consumes all elements.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{1, 2, 3, 4}
	iter := itertools.NewSliceIterator(s)

	fmt.Println(iter.Count())
}
Output:

4

func (*Iterator[T]) Drop

func (i *Iterator[T]) Drop(n int) int

Drop skips next n elements in iterator, returning amount of skipped elements (if iterator has fewer elements than n, returned value is equal to the amount of elements).

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	const skipNextTwo = 0x01

	data := []byte{'H', 'e', 'l', 'l', 'o', 0x01, 'W', 'o', 'r', 'l', 'd'}

	iter := itertools.NewSliceIterator(data)

	for iter.Next() {
		elem := iter.Elem()
		if elem == skipNextTwo {
			dropped := iter.Drop(2)
			fmt.Printf("dropped %d elements\n", dropped)
		} else {
			fmt.Printf("%c\n", elem)
		}
	}
}
Output:

H
e
l
l
o
dropped 2 elements
r
l
d

func (*Iterator[T]) Elem

func (i *Iterator[T]) Elem() T

Elem returns the current element of iterator. If iterator is empty (Next returns false), result is unspecified.

func (*Iterator[T]) Filter

func (i *Iterator[T]) Filter(f func(T) bool) *Iterator[T]

Filter produces new iterator that yields only elements for which function f returns true.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{1, 2, 3, 4, 5, 6, 7}

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

	evenFilter := func(n int) bool {
		return n%2 == 0
	}

	oddIter := itertools.NewSliceIterator(s).Filter(oddFilter)
	evenIter := itertools.NewSliceIterator(s).Filter(evenFilter)

	fmt.Println(oddIter.Collect())
	fmt.Println(evenIter.Collect())

}
Output:

[1 3 5 7]
[2 4 6]

func (*Iterator[T]) Limit

func (i *Iterator[T]) Limit(size int) *Iterator[T]

Limit produces new iterator that can return at most size elements.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	iter := itertools.NewAsciiIterator("Hello World!").Limit(5)

	for iter.Next() {
		fmt.Printf("%c\n", iter.Elem())
	}
}
Output:

H
e
l
l
o

func (*Iterator[T]) Max

func (i *Iterator[T]) Max(f func(T, T) int) T

Max returns max element of iterator, using provided comparison function. Comparison function returns next results:

  • -1: if the first argument is less than second one
  • 0: if two arguments are equal
  • 1: if the first argument is greater than second one
Example
package main

import (
	"cmp"
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	type Person struct {
		Name string
		Age  uint
	}

	people := []Person{
		{"Bob", 31},
		{"John", 42},
		{"Michael", 17},
		{"Jenny", 26},
	}

	iter := itertools.NewSliceIterator(people)

	oldest := iter.Max(func(a Person, b Person) int {
		return cmp.Compare(a.Age, b.Age)
	})

	fmt.Printf("Oldest person: %s (age %d)\n", oldest.Name, oldest.Age)
}
Output:

Oldest person: John (age 42)

func (*Iterator[T]) Next

func (i *Iterator[T]) Next() bool

Next proceeds iterator to the next element, returning boolean value to show that said element exists.

func (*Iterator[T]) Range

func (i *Iterator[T]) Range(f func(T) bool)

Range calls function f for every element of iterator until the function returns false

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []string{"First", "Second", "Third", "Fourth", "Fifth"}

	iter := itertools.NewSliceIterator(s)

	iter.Range(func(s string) bool {
		fmt.Println(s)
		return true
	})

}
Output:

First
Second
Third
Fourth
Fifth
Example (Conditional_stop)
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{5, 4, 2, 0, -1, -8, -16}

	iter := itertools.NewSliceIterator(s)

	iter.Range(func(n int) bool {
		fmt.Println(n)
		if n < 0 {
			return false
		}
		return true
	})
}
Output:

5
4
2
0
-1

func (*Iterator[T]) Reduce

func (i *Iterator[T]) Reduce(acc T, f func(acc T, elem T) T) T

Reduce applies given function f to every element of iterator, using previous accumulating state and returning updated accumulating state on each iteration. Reduce also accepts initial value for accumulating state. Reduce returns final accumulating state created after applying f to all elements of iterator.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	sum := func(acc int, n int) int {
		return acc + n
	}

	iter := itertools.NewSliceIterator(s)
	fmt.Println("Sum of slice:", iter.Reduce(0, sum))
}
Output:

Sum of slice: 55

func (*Iterator[T]) WithStep

func (i *Iterator[T]) WithStep(step int) *Iterator[T]

WithStep produces new iterator that yields every "step"th element of underlying iterator If step is non-positive, returns empty iterator.

Example
package main

import (
	"fmt"
	"github.com/KSpaceer/itertools"
)

func main() {
	s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	iter := itertools.NewSliceIterator(s).WithStep(2)

	for iter.Next() {
		fmt.Println(iter.Elem())
	}
}
Output:

1
3
5
7
9

type Pair

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

Pair is 2-size tuple of heterogeneous values.

func (Pair[T, U]) Unpack

func (p Pair[T, U]) Unpack() (T, U)

Unpack returns values of Pair as tuple.

type Summable

type Summable interface {
	constraints.Integer | constraints.Float | constraints.Complex | ~string
}

Directories

Path Synopsis
Package erroriter provides ErrorIterator type and methods to work with iterations possibly containing errors.
Package erroriter provides ErrorIterator type and methods to work with iterations possibly containing errors.

Jump to

Keyboard shortcuts

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