iterators

package
v0.251.0 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2024 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

package iterators provide iterator implementations.

Summary

An Iterator's goal is to decouple the origin of the data from the consumer who uses that data. Most commonly, iterators hide whether the data comes from a specific database, standard input, or elsewhere. This approach helps to design data consumers that are not dependent on the concrete implementation of the data source, while still allowing for the composition and various actions on the received data stream. An Iterator represents an iterable list of element, which length is not known until it is fully iterated, thus can range from zero to infinity. As a rule of thumb, if the consumer is not the final destination of the data stream, it should use the pipeline pattern to avoid bottlenecks with local resources such as memory.

Resources

https://en.wikipedia.org/wiki/Iterator_pattern https://en.wikipedia.org/wiki/Pipeline_(software)

Index

Examples

Constants

View Source
const Break errorkit.Error = `iterators:break`

Variables

This section is empty.

Functions

func Collect

func Collect[T any](i Iterator[T]) (vs []T, err error)

func Count

func Count[T any](i Iterator[T]) (total int, err error)

Count will iterate over and count the total iterations number

Good when all you want is count all the elements in an iterator but don't want to do anything else.

func First

func First[T any](i Iterator[T]) (value T, found bool, err error)

First decode the first next value of the iterator and close the iterator

func ForEach

func ForEach[T any](i Iterator[T], fn func(T) error) (rErr error)

func Last

func Last[T any](i Iterator[T]) (value T, found bool, err error)

func Must

func Must[T any](v T, err error) T

func Pipe

func Pipe[T any]() (*PipeIn[T], *PipeOut[T])

Pipe return a receiver and a sender. This can be used with resources that

Example
var (
	i *iterators.PipeIn[int]
	o *iterators.PipeOut[int]
)

i, o = iterators.Pipe[int]()
_ = i // use it to send values
_ = o // use it to consume values on each iteration (iter.Next())

func Reduce

func Reduce[
	R, T any,
	FN func(R, T) R |
		func(R, T) (R, error),
](i Iterator[T], initial R, blk FN) (result R, rErr error)
Example
raw := iterators.Slice([]string{"1", "2", "42"})

_, _ = iterators.Reduce[[]int](raw, nil, func(vs []int, raw string) ([]int, error) {

	v, err := strconv.Atoi(raw)
	if err != nil {
		return nil, err
	}
	return append(vs, v), nil

})

func Take added in v0.243.0

func Take[T any](iter Iterator[T], n int) ([]T, error)

Take will take up to `n` amount of element, if it is available.

Types

type CallbackOption

type CallbackOption interface {
	// contains filtered or unexported methods
}

func OnClose

func OnClose(fn func() error) CallbackOption

type Iterator

type Iterator[V any] interface {
	// Closer is required to make it able to cancel iterators where resources are being used behind the scene
	// for all other cases where the underling io is handled on a higher level, it should simply return nil
	io.Closer
	// Err return the error cause.
	Err() error
	// Next will ensure that Value returns the next item when executed.
	// If the next value is not retrievable, Next should return false and ensure Err() will return the error cause.
	Next() bool
	// Value returns the current value in the iterator.
	// The action should be repeatable without side effects.
	Value() V
}

Iterator define a separate object that encapsulates accessing and traversing an aggregate object. Clients use an iterator to access and traverse an aggregate without knowing its representation (data structures). Interface design inspirited by https://golang.org/pkg/encoding/json/#Decoder https://en.wikipedia.org/wiki/Iterator_pattern

Example
var iter iterators.Iterator[int]
defer iter.Close()
for iter.Next() {
	v := iter.Value()
	_ = v
}
if err := iter.Err(); err != nil {
	// handle error
}

func Batch

func Batch[T any](iter Iterator[T], size int) Iterator[[]T]

func BatchWithTimeout

func BatchWithTimeout[T any](i Iterator[T], size int, timeout time.Duration) Iterator[[]T]

func BufioScanner

func BufioScanner[T string | []byte](s *bufio.Scanner, closer io.Closer) Iterator[T]

func Empty

func Empty[T any]() Iterator[T]

Empty iterator is used to represent nil result with Null object pattern

Example
iterators.Empty[any]()

func Error

func Error[T any](err error) Iterator[T]

Error returns an Interface that only can do is returning an Err and never have next element

func Errorf

func Errorf[T any](format string, a ...interface{}) Iterator[T]

Errorf behaves exactly like fmt.Errorf but returns the error wrapped as iterator

func Filter

func Filter[T any](i Iterator[T], filter func(T) bool) Iterator[T]
Example
var iter iterators.Iterator[int]
iter = iterators.Slice([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
iter = iterators.Filter[int](iter, func(n int) bool { return n > 2 })

defer iter.Close()
for iter.Next() {
	n := iter.Value()
	_ = n
}
if err := iter.Err(); err != nil {
	log.Fatal(err)
}

func Func

func Func[T any](next func() (v T, ok bool, err error), callbackOptions ...CallbackOption) Iterator[T]

Func enables you to create an iterator with a lambda expression. Func is very useful when you have to deal with non type safe iterators that you would like to map into a type safe variant. In case you need to close the currently mapped resource, use the OnClose callback option.

func Head[T any](iter Iterator[T], n int) Iterator[T]

Head takes the first n element, similarly how the coreutils "head" app works.

Example
infStream := iterators.Func[int](func() (v int, ok bool, err error) {
	return 42, true, nil
})

i := iterators.Head(infStream, 3)

vs, err := iterators.Collect(i)
_, _ = vs, err // []{42, 42, 42}, nil

func Limit

func Limit[V any](iter Iterator[V], n int) Iterator[V]

func Map

func Map[To any, From any](iter Iterator[From], transform func(From) (To, error)) Iterator[To]

Map allows you to do additional transformation on the values. This is useful in cases, where you have to alter the input value, or change the type all together. Like when you read lines from an input stream, and then you map the line content to a certain data structure, in order to not expose what steps needed in order to deserialize the input stream, thus protect the business rules from this information.

Example
rawNumbers := iterators.Slice([]string{"1", "2", "42"})
numbers := iterators.Map[int](rawNumbers, strconv.Atoi)
_ = numbers

func Merge added in v0.243.0

func Merge[T any](iters ...Iterator[T]) Iterator[T]

func Offset

func Offset[V any](iter Iterator[V], offset int) Iterator[V]

func Paginate

func Paginate[T any](
	ctx context.Context,
	more func(ctx context.Context, offset int) (values []T, hasNext bool, _ error),
) Iterator[T]

Paginate will create an Iterator[T] which can be used like any other iterator, Under the hood the "more" function will be used to dynamically retrieve more values when the previously called values are already used up.

If the more function has a hard-coded true for the "has next page" return value, then the pagination will interpret an empty result as "no more pages left".

Example
ctx := context.Background()
fetchMoreFoo := func(ctx context.Context, offset int) ([]Foo, bool, error) {
	const limit = 10
	query := url.Values{}
	query.Set("limit", strconv.Itoa(limit))
	query.Set("offset", strconv.Itoa(offset))
	resp, err := http.Get("https://api.mydomain.com/v1/foos?" + query.Encode())
	if err != nil {
		return nil, false, err
	}

	var values []FooDTO
	defer resp.Body.Close()
	dec := json.NewDecoder(resp.Body)
	dec.DisallowUnknownFields()
	if err := dec.Decode(&values); err != nil {
		return nil, false, err
	}

	vs, err := dtokit.Map[[]Foo](ctx, values)
	if err != nil {
		return nil, false, err
	}
	probablyHasNextPage := len(vs) == limit
	return vs, probablyHasNextPage, nil
}

foos := iterators.Paginate(ctx, fetchMoreFoo)
_ = foos // foos can be called like any iterator,
// and under the hood, the fetchMoreFoo function will be used dynamically,
// to retrieve more values when the previously called values are already used up.

func SingleValue

func SingleValue[T any](v T) Iterator[T]

SingleValue creates an iterator that can return one single element and will ensure that Next can only be called once.

func Slice

func Slice[T any](slice []T) Iterator[T]

func WithCallback

func WithCallback[T any](i Iterator[T], cs ...CallbackOption) Iterator[T]

func WithConcurrentAccess

func WithConcurrentAccess[T any](i Iterator[T]) Iterator[T]

WithConcurrentAccess allows you to convert any iterator into one that is safe to use from concurrent access. The caveat with this is that this protection only allows 1 Decode call for each Next call.

func WithErr added in v0.243.0

func WithErr[T any](iter Iterator[T], err error) Iterator[T]

type PipeIn

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

PipeIn provides access to feed a pipe receiver with entities

func (*PipeIn[T]) Close

func (in *PipeIn[T]) Close() error

Close will close the feed and err channels, which eventually notify the receiver that no more value expected

func (*PipeIn[T]) Error

func (in *PipeIn[T]) Error(err error)

Error send an error object to the PipeOut side, so it will be accessible with iterator.Err()

func (*PipeIn[T]) Value

func (in *PipeIn[T]) Value(v T) (ok bool)

Value sends value to the PipeOut.Value. It returns if sending was possible.

type PipeOut

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

PipeOut implements iterator interface while it's still being able to receive values, used for streaming

func (*PipeOut[T]) Close

func (out *PipeOut[T]) Close() error

Close sends a signal back that no more value should be sent because receiver stops listening

func (*PipeOut[T]) Err

func (out *PipeOut[T]) Err() error

Err returns an error object that the pipe sender wants to present for the pipe receiver

func (*PipeOut[T]) Next

func (out *PipeOut[T]) Next() bool

Next set the current entity for the next value returns false if no next value

func (*PipeOut[T]) Value

func (out *PipeOut[T]) Value() T

Value will link the current buffered value to the pointer value that is given as "e"

type StubIter

type StubIter[T any] struct {
	Iterator  Iterator[T]
	StubValue func() T
	StubClose func() error
	StubNext  func() bool
	StubErr   func() error
}

func Stub

func Stub[T any](i Iterator[T]) *StubIter[T]

func (*StubIter[T]) Close

func (m *StubIter[T]) Close() error

func (*StubIter[T]) Err

func (m *StubIter[T]) Err() error

func (*StubIter[T]) Next

func (m *StubIter[T]) Next() bool

func (*StubIter[T]) ResetClose

func (m *StubIter[T]) ResetClose()

func (*StubIter[T]) ResetErr

func (m *StubIter[T]) ResetErr()

func (*StubIter[T]) ResetNext

func (m *StubIter[T]) ResetNext()

func (*StubIter[T]) ResetValue

func (m *StubIter[T]) ResetValue()

func (*StubIter[T]) Value

func (m *StubIter[T]) Value() T

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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