iterutils

package module
v0.0.0-...-05d8a49 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2024 License: MIT Imports: 1 Imported by: 0

README

iterutils

Go Reference

iterutils is a Go library that provides utilities for working with iterators (iter.Seq and iter.Seq2). It offers various functions to simplify iteration, transform sequences, and handle concurrent operations. This library is designed to enhance the flexibility and performance of your Go applications by providing easy-to-use iterator utilities.

Installation

To install iterutils, use go get:

go get github.com/mackee/iterutils

Features

  • FromNexter: Convert traditional iteration objects into iter.Seq.
  • FromNexter2: Convert traditional iteration objects returning pairs of values into iter.Seq2.
  • FromTryNexter2: Handle iteration with error handling, converting objects that return both a value and an error into iter.Seq2.
  • NexterWithT: Wrap existing iteration types to allow their use with functions like FromNexter.
  • TryNexterWithT: Wrap iteration types that return values and errors, allowing their use with FromTryNexter2.
  • async.Map: Apply a function to each element of an iterator asynchronously.
  • async.Map2: Apply a function to each pair of elements from a sequence asynchronously.

Usage

Basic Example
package main

import (
    "fmt"
    "github.com/mackee/iterutils"
    "github.com/mackee/iterutils/async"
)

func main() {
    // MyNexter is an object that implements the Next() method
    myNexter := &MyNexter{}
    // MyTransform is a function that transforms the value returned by MyNexter.Next()
    itr := iterutils.FromNexter(myNexter, myTransform)
    // myAsyncTransform is a function that takes lots of time to process but can be executed concurrently
    asyncIter := async.Map(itr, myAsyncTransform)

    for elem := range asyncSeq {
        fmt.Println(elem)
    }
}
FromNexter

Converts a traditional iterator object into an iter.Seq. This is useful for wrapping objects like database/sql.Rows or bufio.Scanner to be used in an iter.Seq.

FromNexter2

Similar to FromNexter, but for iterators that return pairs of values, converting them into an iter.Seq2.

FromTryNexter2

Handles iterators that return both a value and an error, converting them into an iter.Seq2. This is useful for handling operations like reading from a file or a network stream, where each iteration may result in an error.

async.Map

Applies a function asynchronously to each element of an iterator. This is especially useful when dealing with time-consuming operations that can be executed concurrently. The order of the results in the returned iter.Seq is maintained.

async.Map2

Similar to async.Map, but applies the function to each pair of elements from an iter.Seq2. The order of the pairs in the returned iter.Seq2 is maintained.

License

This library is licensed under the MIT License. See the LICENSE file for details.

Contributing

Contributions are welcome! Feel free to submit issues or pull requests to improve the library.

Author

This library is developed and maintained by mackee.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FromNexter

func FromNexter[T Nexter, U any](n T, fn func(T) U) iter.Seq[U]

FromNexter converts a Nexter to an iter.Seq.

Nexter is intended for converting traditional iteration objects, such as database/sql.Rows or bufio.Scanner, into an iter.Seq. This function takes a Nexter `n` and a transformation function `fn`, applying `fn` to each element produced by `n` and yielding the results to the sequence.

The iteration continues as long as `n.Next()` returns true and the `yield` function returns true. If `yield` returns false, the iteration stops early.

Example
package main

import (
	"bufio"
	"fmt"
	"strings"

	"github.com/mackee/iterutils"
)

func main() {
	s := "foo\nbar\nbaz\n"
	scanner := bufio.NewScanner(strings.NewReader(s))
	nexter := iterutils.NewNexterWithT(scanner, func(scanner *bufio.Scanner) bool {
		return scanner.Scan()
	})
	it := iterutils.FromNexter(nexter, func(nexter iterutils.NexterWithT[*bufio.Scanner]) string {
		return nexter.T().Text()
	})
	for s := range it {
		fmt.Println(s)
	}
}
Output:

foo
bar
baz

func FromNexter2

func FromNexter2[T Nexter, U any, V any](n T, fn func(T) (U, V)) iter.Seq2[U, V]

FromNexter2 converts a Nexter to an iter.Seq2.

This function is similar to FromNexter, but it allows the transformation function `fn` to return two values (`U` and `V`). These values are then yielded together to the sequence. This is useful when each iteration produces a pair of related values.

The iteration continues as long as `n.Next()` returns true and the `yield` function returns true. If `yield` returns false, the iteration stops early.

Example
package main

import (
	"context"
	"database/sql"
	"database/sql/driver"
	"fmt"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/mackee/iterutils"
)

func mockDB() *sql.DB {
	db, mock, err := sqlmock.New()
	if err != nil {
		panic(err)
	}
	rowData := [][]driver.Value{
		{1, "foo"},
		{2, "bar"},
		{3, "baz"},
	}
	rows := mock.NewRows([]string{"id", "name"}).AddRows(rowData...)
	mock.ExpectQuery("SELECT id, name FROM users").WillReturnRows(rows)
	return db
}

func main() {
	db := mockDB()

	ctx := context.Background()
	rows, err := db.QueryContext(ctx, "SELECT id, name FROM users")
	if err != nil {
		panic(err)
	}
	defer rows.Close()

	type user struct {
		ID   int
		Name string
	}
	users := iterutils.FromNexter2(rows, func(rows *sql.Rows) (*user, error) {
		var u user
		if err := rows.Scan(&u.ID, &u.Name); err != nil {
			return nil, err
		}
		return &u, nil
	})
	for users, err := range users {
		if err != nil {
			panic(err)
		}
		fmt.Println(users.ID, users.Name)
	}
}
Output:

1 foo
2 bar
3 baz

func FromTryNexter2

func FromTryNexter2[V any, T TryNexter[V], U any, W any](t T, fn func(T, V, error) (U, W)) iter.Seq2[U, W]

FromTryNexter2 can be applied to types that return both a value and an error during iteration, such as encoding/csv.Reader.

This function converts a TryNexter to an iter.Seq2, allowing you to iterate over a sequence of two values. The `fn` function takes the TryNexter `t`, the value `v`, and the error `err` returned by `t.Next()`, and it produces two values (`U` and `W`) that are yielded to the sequence.

The iteration continues until the yield function returns false.

Example
package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/mackee/iterutils"
)

func main() {
	csvText := `id,name,created_at
1,foo,2021-01-01 09:00:00
2,bar,2021-01-02 12:00:00
3,baz,2021-01-03 15:00:00
`
	csvr := csv.NewReader(strings.NewReader(csvText))
	// Skip the header row
	if _, err := csvr.Read(); err != nil {
		panic(err)
	}
	tn := iterutils.NewTryNexterWithT(csvr, func(csvr *csv.Reader) (record []string, err error) {
		return csvr.Read()
	})
	type user struct {
		ID        int
		Name      string
		CreatedAt time.Time
	}
	it := iterutils.FromTryNexter2(tn, func(t iterutils.TryNexterWithT[*csv.Reader, []string], record []string, err error) (*user, error) {
		if err != nil {
			return nil, err
		}
		if len(record) != 3 {
			line, _ := t.T().FieldPos(0)
			return nil, fmt.Errorf("expected 3 fields, got %d, lines=%d", len(record), line)
		}
		id, err := strconv.Atoi(record[0])
		if err != nil {
			return nil, err
		}
		createdAt, err := time.Parse(time.DateTime, record[2])
		if err != nil {
			return nil, err
		}
		return &user{
			ID:        id,
			Name:      record[1],
			CreatedAt: createdAt,
		}, nil
	})
	for u, err := range it {
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
		fmt.Println(u.ID, u.Name, u.CreatedAt)
	}
}
Output:

1 foo 2021-01-01 09:00:00 +0000 UTC
2 bar 2021-01-02 12:00:00 +0000 UTC
3 baz 2021-01-03 15:00:00 +0000 UTC

Types

type Nexter

type Nexter interface {
	Next() bool
}

type NexterWithT

type NexterWithT[T any] interface {
	Nexter
	T() T
}

NexterWithT is used to convert a type T that performs traditional iteration into a Nexter.

This interface provides a mechanism to wrap traditional iterators that work with a specific type T, allowing them to be used as Nexters.

func NewNexterWithT

func NewNexterWithT[T any](t T, next func(T) bool) NexterWithT[T]

NewNexter creates a new NexterWithT instance.

This function accepts an initial value of type T and a `next` function that determines the continuation of the iteration. The returned NexterWithT can be used to iterate over the value of type T, with the current value accessible via the T() method.

type TryNexter

type TryNexter[V any] interface {
	Next() (V, error)
}

TryNexter is an interface for iterators that return both a value of type V and an error during iteration.

This interface is commonly used for iterators where each iteration may result in an error, such as reading from a file or a network stream.

type TryNexterWithT

type TryNexterWithT[T any, V any] interface {
	TryNexter[V]
	T() T
}

TryNexterWithT is an interface that is created exclusively through the NewTryNexterWithT function.

This interface is designed to wrap existing iteration types that return both a value and an error, allowing them to be used with FromTryNexter2. In addition to the iteration, it provides access to the current state or context of type T via the T() method.

func NewTryNexterWithT

func NewTryNexterWithT[T any, V any](t T, next func(T) (V, error)) TryNexterWithT[T, V]

NewTryNexterWithT creates a new TryNexterWithT instance.

This function takes an initial value of type T and a `next` function that controls the iteration. The `next` function returns a value of type V and an error. The returned TryNexterWithT allows iteration with error handling, while also providing access to the current state via the T() method.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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