-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathiterator.go
140 lines (130 loc) · 3.71 KB
/
iterator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package iter
import "errors"
// Error returned from Iterator.Next() when iteration is done.
var FINISHED = errors.New("FINISHED")
// Iterator letting you specify just two functions defining iteration. All
// helper functions utilize this.
//
// Iterator is designed to avoid common pitfalls and to allow common Go patterns:
// - errors can be yielded all the way up the chain to the caller
// - safe iteration in background channels
// - iterators are nil-clean (you can send nil values through the iterator)
// - allows for cleanup when iteration ends early
type Iterator struct {
// Get the next value (or error).
// nil, iter.FINISHED indicates iteration is complete.
Next func() (interface{}, error)
// Close out resources in case iteration terminates early. Callers are *not*
// required to call this if iteration completes cleanly (but they may).
Close func()
}
// Use to create an iterator from a single closure. retun nil to stop iteration.
//
// iter.NewSimple(func() interface{} { return x*2 })
func NewSimple(f func() interface{}) Iterator {
return Iterator{
Next: func() (interface{}, error) {
val := f()
if val == nil {
return nil, FINISHED
} else {
return f, nil
}
},
Close: func() {},
}
}
// Transform values in the input.
// iterator.Map(func(item interface{}) interface{} { item.(int) * 2 })
func (iterator Iterator) Map(mapper func(interface{}) interface{}) Iterator {
return Iterator{
Next: func() (interface{}, error) {
item, err := iterator.Next()
if err != nil {
return nil, err
}
return mapper(item), nil
},
Close: func() { iterator.Close() },
}
}
// Filter values from the input.
// iterator.Select(func(item interface{}) bool { item.(int) > 10 })
func (iterator Iterator) Select(selector func(interface{}) bool) Iterator {
return Iterator{
Next: func() (interface{}, error) {
for {
item, err := iterator.Next()
if err != nil {
return nil, err
}
if selector(item) {
return item, nil
}
}
},
Close: func() { iterator.Close() },
}
}
// Concatenate multiple iterators together in one.
// d := iter.Concat(a, b, c)
func Concat(iterators ...Iterator) Iterator {
i := 0
return Iterator{
Next: func() (interface{}, error) {
if i >= len(iterators) {
return nil, FINISHED
}
item, err := iterators[i].Next()
if err == FINISHED {
i++
return nil, err
} else if err != nil {
i = len(iterators)
return nil, err
}
return item, nil
},
Close: func() {
// Close out remaining iterators
for ; i < len(iterators); i++ {
iterators[i].Close()
}
},
}
}
// Iterate over all values, calling a user-defined function for each one.
// iterator.Each(func(item interface{}) { fmt.Println(item) })
func (iterator Iterator) Each(processor func(interface{})) error {
defer iterator.Close()
item, err := iterator.Next()
for err == nil {
processor(item)
item, err = iterator.Next()
}
return err
}
// Iterate over all values, calling a user-defined function for each one.
// If an error is returned from the function, iteration terminates early
// and the EachWithError function returns the error.
// iterator.EachWithError(func(item interface{}) error { return http.Get(item.(string)) })
func (iterator Iterator) EachWithError(processor func(interface{}) error) error {
defer iterator.Close()
item, err := iterator.Next()
for err == nil {
err = processor(item)
if err == nil {
item, err = iterator.Next()
}
}
return err
}
// Convert the iteration directly to a slice.
// var list []interface{}
// list, err := iterator.ToSlice()
func (iterator Iterator) ToSlice() (list []interface{}, err error) {
err = iterator.Each(func(item interface{}) {
list = append(list, item)
})
return list, err
}