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 ¶
- func Walk(value interface{}, f func(el Pair) error) error
- type Iter
- func (it Iter) IterChan(val reflect.Value, f func(seq int, recv reflect.Value) error) error
- func (it Iter) IterMap(val reflect.Value, f func(key, val reflect.Value) error) error
- func (it Iter) IterSlice(val reflect.Value, f func(idx int, val reflect.Value) error) error
- func (it Iter) IterStruct(val reflect.Value, f func(field reflect.StructField, val reflect.Value) error) error
- type Iterator
- type Pair
- type Walker
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Walk ¶
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 ¶
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 ¶
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.
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 NewRecoverIter ¶
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().