iterator.go

v0.2.0
Doc Versions Source
1
package collect
2
3
import (
4
	"reflect"
5
	"slices"
6
)
7
8
// ListGenerator produces values one at a time. It returns (value, true) for
9
// each element and (zero, false) when the sequence is exhausted.
10
type ListGenerator[E any] func() (E, bool)
11
12
// ListIterator is a lazy, pull-based iterator over a sequence of elements.
13
// All intermediate operations (Map, Filter, Sort, Skip, Limit) return a new
14
// iterator; terminal operations (Count, Apply, Collect) consume it.
15
type ListIterator[E any] interface {
16
	// Map returns an iterator that applies f to each element.
17
	Map(f func(E) E) ListIterator[E]
18
	// Filter returns an iterator that yields only elements satisfying pred.
19
	Filter(pred func(E) bool) ListIterator[E]
20
	// Sort collects all elements, sorts them with less, and returns a new iterator.
21
	Sort(less func(E, E) bool) ListIterator[E]
22
	// Skip returns an iterator that discards the first count elements.
23
	// Panics if count is negative.
24
	Skip(count int) ListIterator[E]
25
	// Limit returns an iterator that yields at most count elements.
26
	// Panics if count is negative.
27
	Limit(count int) ListIterator[E]
28
	// Count exhausts the iterator and returns the number of elements.
29
	Count() int
30
	// Apply exhausts the iterator calling f for each element and returns the count.
31
	Apply(f func(E)) int
32
	// Collect collects all remaining elements into a slice.
33
	Collect() []E
34
	// MapIndex returns an iterator that applies f with the 0-based index to each element.
35
	MapIndex(f func(int, E) E) ListIterator[E]
36
	// FilterIndex returns an iterator that yields only elements where pred(index, elem) is true.
37
	FilterIndex(pred func(int, E) bool) ListIterator[E]
38
	// Unique returns an iterator that deduplicates elements using the string key returned by fn.
39
	Unique(fn func(E) string) ListIterator[E]
40
	// ApplyIndex exhausts the iterator calling f with the 0-based index for each element.
41
	ApplyIndex(f func(int, E))
42
	// Materialize materializes the iterator and returns a new iterator over the result.
43
	Materialize() ListIterator[E]
44
}
45
46
type listIterator[E any] struct {
47
	generator   ListGenerator[E]
48
	maxElements int
49
}
50
51
// NewListIterator creates a [ListIterator] backed by gen. maxElements is a
52
// hint for the expected upper bound of elements (used for pre-allocation);
53
// pass -1 if unknown.
54
func NewListIterator[E any](gen ListGenerator[E], maxElements int) ListIterator[E] {
55
	return &listIterator[E]{generator: gen, maxElements: maxElements}
56
}
57
58
// FromSlice creates a [ListIterator] that yields the elements of inp
59
// in order.
60
func FromSlice[E any](inp []E) ListIterator[E] {
61
	pos := 0
62
	gen := func() (E, bool) {
63
		if pos >= len(inp) {
64
			var zero E
65
			return zero, false
66
		}
67
		v := inp[pos]
68
		pos++
69
		return v, true
70
	}
71
	return &listIterator[E]{generator: gen, maxElements: len(inp)}
72
}
73
74
// FromSliceWith creates a [ListIterator] that maps each element of inp through cb.
75
func FromSliceWith[E any, I any](inp []E, cb func(E) I) ListIterator[I] {
76
	pos := 0
77
	gen := func() (I, bool) {
78
		if pos >= len(inp) {
79
			var zero I
80
			return zero, false
81
		}
82
		v := cb(inp[pos])
83
		pos++
84
		return v, true
85
	}
86
	return &listIterator[I]{generator: gen, maxElements: len(inp)}
87
}
88
89
// FromMapKeys creates a [ListIterator] over the keys of a map.
90
func FromMapKeys[K comparable, V any](inp map[K]V) ListIterator[K] {
91
	rv := reflect.ValueOf(inp)
92
	keys := rv.MapKeys()
93
	pos := 0
94
	gen := func() (K, bool) {
95
		if pos >= len(keys) {
96
			var zero K
97
			return zero, false
98
		}
99
		v := keys[pos].Interface().(K)
100
		pos++
101
		return v, true
102
	}
103
	return &listIterator[K]{generator: gen, maxElements: len(keys)}
104
}
105
106
// FromMapValues creates a [ListIterator] over the values of a map.
107
func FromMapValues[K comparable, V any](inp map[K]V) ListIterator[V] {
108
	rv := reflect.ValueOf(inp)
109
	keys := rv.MapKeys()
110
	pos := 0
111
	gen := func() (V, bool) {
112
		if pos >= len(keys) {
113
			var zero V
114
			return zero, false
115
		}
116
		v := rv.MapIndex(keys[pos]).Interface().(V)
117
		pos++
118
		return v, true
119
	}
120
	return &listIterator[V]{generator: gen, maxElements: len(keys)}
121
}
122
123
func (it *listIterator[E]) Map(f func(E) E) ListIterator[E] {
124
	gen := it.generator
125
	return &listIterator[E]{
126
		generator: func() (E, bool) {
127
			v, ok := gen()
128
			if !ok {
129
				return v, false
130
			}
131
			return f(v), true
132
		},
133
		maxElements: it.maxElements,
134
	}
135
}
136
137
func (it *listIterator[E]) Filter(pred func(E) bool) ListIterator[E] {
138
	gen := it.generator
139
	return &listIterator[E]{
140
		generator: func() (E, bool) {
141
			for {
142
				v, ok := gen()
143
				if !ok {
144
					return v, false
145
				}
146
				if pred(v) {
147
					return v, true
148
				}
149
			}
150
		},
151
		maxElements: it.maxElements,
152
	}
153
}
154
155
func (it *listIterator[E]) Sort(less func(E, E) bool) ListIterator[E] {
156
	all := it.Collect()
157
	slices.SortFunc(all, func(a, b E) int {
158
		switch {
159
		case less(a, b):
160
			return -1
161
		case less(b, a):
162
			return 1
163
		default:
164
			return 0
165
		}
166
	})
167
	return FromSlice(all)
168
}
169
170
func (it *listIterator[E]) Skip(count int) ListIterator[E] {
171
	if count < 0 {
172
		panic("collect: Skip count must be non-negative")
173
	}
174
	gen := it.generator
175
	skipped := false
176
	return &listIterator[E]{
177
		generator: func() (E, bool) {
178
			if !skipped {
179
				skipped = true
180
				for range count {
181
					if _, ok := gen(); !ok {
182
						var zero E
183
						return zero, false
184
					}
185
				}
186
			}
187
			return gen()
188
		},
189
		maxElements: max(it.maxElements-count, 0),
190
	}
191
}
192
193
func (it *listIterator[E]) Limit(count int) ListIterator[E] {
194
	if count < 0 {
195
		panic("collect: Limit count must be non-negative")
196
	}
197
	gen := it.generator
198
	remaining := count
199
	return &listIterator[E]{
200
		generator: func() (E, bool) {
201
			if remaining <= 0 {
202
				var zero E
203
				return zero, false
204
			}
205
			remaining--
206
			return gen()
207
		},
208
		maxElements: min(it.maxElements, count),
209
	}
210
}
211
212
func (it *listIterator[E]) Count() int {
213
	n := 0
214
	for {
215
		if _, ok := it.generator(); !ok {
216
			return n
217
		}
218
		n++
219
	}
220
}
221
222
func (it *listIterator[E]) Apply(f func(E)) int {
223
	n := 0
224
	for {
225
		v, ok := it.generator()
226
		if !ok {
227
			return n
228
		}
229
		f(v)
230
		n++
231
	}
232
}
233
234
func (it *listIterator[E]) Collect() []E {
235
	sz := it.maxElements
236
	if sz < 0 {
237
		sz = 0
238
	}
239
	rv := make([]E, 0, sz)
240
	for {
241
		v, ok := it.generator()
242
		if !ok {
243
			break
244
		}
245
		rv = append(rv, v)
246
	}
247
	return rv[:len(rv):len(rv)]
248
}
249
250
func (it *listIterator[E]) MapIndex(f func(int, E) E) ListIterator[E] {
251
	gen := it.generator
252
	pos := 0
253
	return &listIterator[E]{
254
		generator: func() (E, bool) {
255
			v, ok := gen()
256
			if !ok {
257
				return v, false
258
			}
259
			result := f(pos, v)
260
			pos++
261
			return result, true
262
		},
263
		maxElements: it.maxElements,
264
	}
265
}
266
267
func (it *listIterator[E]) FilterIndex(pred func(int, E) bool) ListIterator[E] {
268
	gen := it.generator
269
	pos := 0
270
	return &listIterator[E]{
271
		generator: func() (E, bool) {
272
			for {
273
				v, ok := gen()
274
				if !ok {
275
					return v, false
276
				}
277
				idx := pos
278
				pos++
279
				if pred(idx, v) {
280
					return v, true
281
				}
282
			}
283
		},
284
		maxElements: it.maxElements,
285
	}
286
}
287
288
func (it *listIterator[E]) Unique(fn func(E) string) ListIterator[E] {
289
	gen := it.generator
290
	seen := make(map[string]struct{}, it.maxElements)
291
	return &listIterator[E]{
292
		generator: func() (E, bool) {
293
			for {
294
				v, ok := gen()
295
				if !ok {
296
					return v, false
297
				}
298
				key := fn(v)
299
				if _, exists := seen[key]; exists {
300
					continue
301
				}
302
				seen[key] = struct{}{}
303
				return v, true
304
			}
305
		},
306
		maxElements: it.maxElements,
307
	}
308
}
309
310
func (it *listIterator[E]) ApplyIndex(f func(int, E)) {
311
	pos := 0
312
	for {
313
		v, ok := it.generator()
314
		if !ok {
315
			return
316
		}
317
		f(pos, v)
318
		pos++
319
	}
320
}
321
322
func (it *listIterator[E]) Materialize() ListIterator[E] {
323
	return FromSlice(it.Collect())
324
}
325

Source Files