iter

package module
v0.0.0-...-353ca66 Latest Latest
Warning

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

Go to latest
Published: Nov 24, 2016 License: MIT Imports: 2 Imported by: 1

README

Go Package: iter

Go Report Card

Get:

go get -u github.com/cstockton/go-iter

Example:

func Example() {
	v := []interface{}{"a", "b", []string{"c", "d"}}
	err := iter.Walk(v, func(el iter.Pair) error {
		// check for errors
		if err := el.Err(); err != nil {
			return err
		}

		// Halt iteration by returning an error.
		if el.Depth() > 1 {
			return errors.New("Stopping this walk.")
		}

		fmt.Println(el)
		return nil
	})
	if err == nil {
		log.Fatal(err)
	}

	// Output:
	// Pair{(int) 0 => a (string)}
	// Pair{(int) 1 => b (string)}
}

About

Package iter provides primitives for walking arbitrary data structures.

Bugs and Patches

Feel free to report bugs and submit pull requests.

Documentation

Overview

Package iter provides primitives for walking arbitrary data structures.

Example
package main

import (
	"fmt"

	iter "github.com/cstockton/go-iter"
)

func main() {

	// Iter will walk any structured type, chans, slices, maps, structs.
	type Package struct {
		Name     string
		Synopsis string

		// Iter ascends into child structured types as well.
		Parent *Package
	}

	// Iter will visit each key -> value pair. For structs the key will be the
	// reflect.StructField, for maps the element key and the sequence integer for
	// arrays and, slices.
	pkgs := []Package{
		{"io", "Package io provides basic interfaces to I/O primitives.", &Package{
			"ioutil", "Package ioutil implements some I/O utility functions.", nil}},
		{"hash", "Package hash provides interfaces for hash functions.", nil},
		{"flag", "Package flag implements command-line flag parsing.", nil}}

	fmt.Println("Packages:")
	iter.Walk(pkgs, func(el iter.Pair) error {
		fmt.Printf("  %v\n", el)
		return nil
	})

}
Output:

Packages:
  Pair{(reflect.StructField) Name => io (string)}
  Pair{(reflect.StructField) Synopsis => Package io p (string)}
  Pair{(reflect.StructField) Name => ioutil (string)}
  Pair{(reflect.StructField) Synopsis => Package iout (string)}
  Pair{(reflect.StructField) Parent => <nil> (*iter_test.Package)}
  Pair{(reflect.StructField) Name => hash (string)}
  Pair{(reflect.StructField) Synopsis => Package hash (string)}
  Pair{(reflect.StructField) Parent => <nil> (*iter_test.Package)}
  Pair{(reflect.StructField) Name => flag (string)}
  Pair{(reflect.StructField) Synopsis => Package flag (string)}
  Pair{(reflect.StructField) Parent => <nil> (*iter_test.Package)}
Example (Recursion)
package main

import (
	"fmt"
	"reflect"
	"strings"

	iter "github.com/cstockton/go-iter"
)

func main() {

	type exampleWalk struct {
		Head  string
		Child *exampleWalk
		Tail  string
	}
	trnew := func(pnt *exampleWalk, i int) *exampleWalk {
		tr := &exampleWalk{
			Head:  fmt.Sprintf("tail #%d", i),
			Child: pnt,
			Tail:  fmt.Sprintf("tail #%d", i),
		}
		return tr
	}
	tr := trnew(nil, 4)
	for i := 3; i > 0; i-- {
		tr = trnew(tr, i)
	}

	w := iter.NewWalker(&iter.Iter{ChanRecv: true})
	err := w.Walk(tr, func(el iter.Pair) error {
		pad := strings.Repeat("  ", el.Depth())
		k, v := el.Pair()
		if sf, ok := k.(reflect.StructField); ok {
			fmt.Printf("%v%v -> %v\n", pad, sf.Name, v)
		} else {
			fmt.Printf("%v%v -> %v\n", pad, k, v)
		}
		return nil
	})
	if err != nil {
		fmt.Println(err)
	}

}
Output:

Head -> tail #1
    Head -> tail #2
      Head -> tail #3
        Head -> tail #4
        Child -> <nil>
        Tail -> tail #4
      Tail -> tail #3
    Tail -> tail #2
  Tail -> tail #1

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Walk

func Walk(value interface{}, f func(el Pair) error) error

Walk will recursively walk the given interface value as long as an error does not occur. The pair func will be given a interface value for each value visited during walking and is expected to return an error if it thinks the traversal should end. A nil value and error is given to the walk func if an inaccessible value (can't reflect.Interface()) is found.

Walk is called on each element of maps, slices and arrays. If the underlying iterator is configured for channels it receives until one fails. Channels should probably be avoided as ranging over them is more concise.

Example
package main

import (
	"fmt"
	"sort"

	iter "github.com/cstockton/go-iter"
)

func main() {

	var res []string
	m := map[int]string{1: "a", 2: "b", 3: "c"}

	err := iter.Walk(m, func(el iter.Pair) error {
		res = append(res, fmt.Sprintf("%v", el))
		return nil
	})
	if err != nil {
		fmt.Println(err)
	}

	sort.Strings(res) // for test determinism
	for _, v := range res {
		fmt.Println(v)
	}

}
Output:

Pair{(int) 1 => a (string)}
Pair{(int) 2 => b (string)}
Pair{(int) 3 => c (string)}
Example (Errors)
package main

import (
	"errors"
	"fmt"

	iter "github.com/cstockton/go-iter"
)

func main() {

	v := []interface{}{"a", "b", []string{"c", "d"}}
	err := iter.Walk(v, func(el iter.Pair) error {
		// check for errors
		if err := el.Err(); err != nil {
			return err
		}

		// Halt iteration by returning an error.
		if el.Depth() > 1 {
			return errors.New("Stopping this walk.")
		}

		fmt.Println(el)
		return nil
	})
	if err == nil {
		fmt.Println("expected an error")
	}

}
Output:

Pair{(int) 0 => a (string)}
Pair{(int) 1 => b (string)}

Types

type Iter

type Iter struct {
	ChanRecv          bool
	ChanBlock         bool
	ExcludeAnonymous  bool
	ExcludeUnexported bool
}

Iter it a basic implementation of Iterator. It is possible for these methods to panic under certain circumstances. If you want to disable panics write it in a iter.NewrecoverFnIter(Iter{}). Performance cost for Visiting slices is negligible relative to iteration using range. About 3x slower for slices with less than 1k elements, for slices with more than 2k elements it will be around 2x as slow. Iterating maps is only 2-3 times slower for small maps of 100-1k elements. When you start to go above that its deficiencies will start to take a linear tax on runtime proportianite to the number of elements. It's roughly 100x slower at 32k elements. This is because it loads all map keys into memory (implementation of reflect.MapKeys).

func (Iter) IterChan

func (it Iter) IterChan(val reflect.Value, f func(
	seq int, recv reflect.Value) error) error

IterChan will try to receive values from the given channel only if ChanRecv is set to true. If ChanBlock is true IterChan will walk values until the channel has been clocked, otherwise it will use the behavior described in the reflect packages Value.TryRecv. This means when setting ChanBlock it is up to the caller to close the channel to prevent a dead lock. A sequential counter for this iterations receives is returned for parity with structured types.

func (Iter) IterMap

func (it Iter) IterMap(val reflect.Value, f func(
	key, val reflect.Value) error) error

IterMap will visit each key and value of a map.

func (Iter) IterSlice

func (it Iter) IterSlice(val reflect.Value, f func(
	idx int, val reflect.Value) error) error

IterSlice will visit each element of an array or slice. Extending the length of an Array or Slice during iteration may panic.

func (Iter) IterStruct

func (it Iter) IterStruct(val reflect.Value, f func(
	field reflect.StructField, val reflect.Value) error) error

IterStruct will visit each field and value in a struct.

type Iterator

type Iterator interface {
	IterChan(val reflect.Value, f func(seq int, ch reflect.Value) error) error
	IterMap(val reflect.Value, f func(key, val reflect.Value) error) error
	IterSlice(val reflect.Value, f func(idx int, val reflect.Value) error) error
	IterStruct(val reflect.Value,
		f func(field reflect.StructField, val reflect.Value) error) error
}

Iterator is a basic interface for iterating elements of a structured type. It serves as backing for other traversal methods. Iterators are safe for use by multiple Go routines, though the underlying values received in the iteration functions may not be.

func NewIter

func NewIter() Iterator

NewIter returns a new Iter.

func NewRecoverIter

func NewRecoverIter(it Iterator) Iterator

NewRecoverIter returns the given iterator wrapped so that it will not panic under any circumstance, instead returning the panic as an error.

type Pair

type Pair interface {

	// Err will return any error associated with the retrieval of this Pair.
	Err() error

	// Depth returns how many structured elements this Pair is nested within.
	Depth() int

	// Parent will return the parent Pair this Pair is associated to. For
	// example if this Pair belongs to a map type, the parent would contain a
	// Pair with Kind() reflect.Map and a reflect.Value of the map Value. If
	// this is the top most element then it will have no Parent.
	Parent() Pair

	// Key will return the key associated with this value.
	Key() interface{}

	// Val will return the value associated with this key.
	Val() interface{}

	// Pair returns the key and value for this Pair.
	Pair() (interface{}, interface{})
}

Pair represents a dyadic pair of values visited by a Walker. The root Pair will have no Parent() and it is permissible for a Pair to contain only a Key or Val. This means any of this interfaces types with nil zero values could be nil regardless if Err() is non-nil. The Err() field may be populated by the Walker or propagated from a user returned error.

Caveats:

Since channels do not have a sequential number to represent a location
within a finite space a simple numeric counter relative to the first
receive operation within the current block instead. This means that future
calls to the same channel could return a identical sequence number.

Example of elements:

[]int{1, 2} -> []Pair{ {0, 1}, {1, 2} }
map[str]int{"a": 1, "b": 2} -> []Pair{ {"a", 1}, {"b", 2} }

Structs:

Key() will contain a reflect.StructFieldvalue, which may be anonymous or
unexported.
Val() will contain the associated fields current value.

Slices, Arrays and Channels:

Key() will contain a int type representing the elements location within the
sequence.
Val() will contain the element value located at Key().

Maps:

Key() will be a interface value of the maps key used to access the Val().
Val() will be a interface value of the value located at Key().

func NewPair

func NewPair(parent Pair, key, value interface{}, err error) Pair

NewPair returns a new key-value pair to be used by Walkers.

type Walker

type Walker interface {
	Walk(value interface{}, f func(el Pair) error) error
}

A Walker is used to perform a full traversal of each child value of any Go type. Each implementation may use their own algorithm for traversal, giving no guarantee for the order each element is visited in.

func NewWalker

func NewWalker(iterator Iterator) Walker

NewWalker returns a new Walker backed by the given Iterator. It will use a basic dfs traversal and will not visit items that can not be converted to an interface.

Jump to

Keyboard shortcuts

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