@johnhenry/iteration 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIT-LICENSE.txt +20 -0
- package/dist/abort.d.ts +26 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +69 -0
- package/dist/abort.js.map +1 -0
- package/dist/assertions/eventualequal.d.ts +9 -0
- package/dist/assertions/eventualequal.d.ts.map +1 -0
- package/dist/assertions/eventualequal.js +30 -0
- package/dist/assertions/eventualequal.js.map +1 -0
- package/dist/async-channel.d.ts +101 -0
- package/dist/async-channel.d.ts.map +1 -0
- package/dist/async-channel.js +208 -0
- package/dist/async-channel.js.map +1 -0
- package/dist/channel-decorators.d.ts +23 -0
- package/dist/channel-decorators.d.ts.map +1 -0
- package/dist/channel-decorators.js +31 -0
- package/dist/channel-decorators.js.map +1 -0
- package/dist/concurrency.d.ts +43 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +145 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/consumers.d.ts +199 -0
- package/dist/consumers.d.ts.map +1 -0
- package/dist/consumers.js +349 -0
- package/dist/consumers.js.map +1 -0
- package/dist/count.d.ts +37 -0
- package/dist/count.d.ts.map +1 -0
- package/dist/count.js +70 -0
- package/dist/count.js.map +1 -0
- package/dist/empty-iterator.d.ts +15 -0
- package/dist/empty-iterator.d.ts.map +1 -0
- package/dist/empty-iterator.js +16 -0
- package/dist/empty-iterator.js.map +1 -0
- package/dist/eventualequal.d.ts +2 -0
- package/dist/eventualequal.d.ts.map +1 -0
- package/dist/eventualequal.js +54 -0
- package/dist/eventualequal.js.map +1 -0
- package/dist/exhaust.d.ts +7 -0
- package/dist/exhaust.d.ts.map +1 -0
- package/dist/exhaust.js +22 -0
- package/dist/exhaust.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/is-iterator.d.ts +5 -0
- package/dist/is-iterator.d.ts.map +1 -0
- package/dist/is-iterator.js +5 -0
- package/dist/is-iterator.js.map +1 -0
- package/dist/iterator-tools.d.ts +164 -0
- package/dist/iterator-tools.d.ts.map +1 -0
- package/dist/iterator-tools.js +231 -0
- package/dist/iterator-tools.js.map +1 -0
- package/dist/itertools.d.ts +237 -0
- package/dist/itertools.d.ts.map +1 -0
- package/dist/itertools.js +471 -0
- package/dist/itertools.js.map +1 -0
- package/dist/tee.d.ts +18 -0
- package/dist/tee.d.ts.map +1 -0
- package/dist/tee.js +118 -0
- package/dist/tee.js.map +1 -0
- package/dist/transduce.d.ts +79 -0
- package/dist/transduce.d.ts.map +1 -0
- package/dist/transduce.js +37 -0
- package/dist/transduce.js.map +1 -0
- package/dist/transducers.d.ts +123 -0
- package/dist/transducers.d.ts.map +1 -0
- package/dist/transducers.js +212 -0
- package/dist/transducers.js.map +1 -0
- package/package.json +122 -0
- package/readme.md +761 -0
- package/src/abort.ts +80 -0
- package/src/assertions/eventualequal.ts +36 -0
- package/src/async-channel.ts +228 -0
- package/src/channel-decorators.ts +49 -0
- package/src/concurrency.ts +168 -0
- package/src/consumers.ts +425 -0
- package/src/count.ts +72 -0
- package/src/empty-iterator.ts +17 -0
- package/src/eventualequal.ts +54 -0
- package/src/exhaust.ts +26 -0
- package/src/index.ts +17 -0
- package/src/is-iterator.ts +9 -0
- package/src/iterator-tools.ts +290 -0
- package/src/itertools.ts +518 -0
- package/src/tee.ts +136 -0
- package/src/transduce.ts +161 -0
- package/src/transducers.ts +256 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { isAsyncIterator } from "./is-iterator.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sentinel returned by a reducer step to stop consumption of the source
|
|
5
|
+
* iterator (early termination -- see the `take` transducer).
|
|
6
|
+
*/
|
|
7
|
+
export const HALT: unique symbol = Symbol("HALT");
|
|
8
|
+
export type Halt = typeof HALT;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Misspelled legacy alias of {@link HALT} (pre-2.1 name).
|
|
12
|
+
* Kept for backwards compatibility; prefer HALT.
|
|
13
|
+
*/
|
|
14
|
+
export { HALT as HAULT };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The transducer step protocol (v2.1, "pending-emission buffer").
|
|
18
|
+
*
|
|
19
|
+
* The accumulator is a plain array used as a pending-emission buffer: the
|
|
20
|
+
* innermost step ("emit" -- see transduce.ts) pushes emitted items onto it
|
|
21
|
+
* and returns it. A step may instead return {@link HALT} to terminate
|
|
22
|
+
* consumption of the source early. A stateful step (e.g. `group`,
|
|
23
|
+
* `partitionBy`) may carry a `complete` method, invoked once after the
|
|
24
|
+
* source is exhausted (or halted) to flush buffered state; `complete`
|
|
25
|
+
* implementations must cascade to their inner step's own `complete` (see
|
|
26
|
+
* transducers.ts).
|
|
27
|
+
*/
|
|
28
|
+
export interface ReducerStep<in In> {
|
|
29
|
+
(buffer: unknown[], item: In, iterator?: Iterable<In> | AsyncIterable<In>): unknown[] | Halt;
|
|
30
|
+
complete?: (buffer: unknown[]) => unknown[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A transducer: transforms a step that consumes `Out` items into a step
|
|
35
|
+
* that consumes `In` items. Produced by the factories in transducers.ts
|
|
36
|
+
* (map, filter, take, group, ...), consumed by transduceSync/transduceAsync
|
|
37
|
+
* (left-to-right composition as written).
|
|
38
|
+
*/
|
|
39
|
+
export type Transducer<In, Out> = (next: ReducerStep<Out>) => ReducerStep<In>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a promise that fulfills after a given number of milliseconds
|
|
43
|
+
* The primary purpose of this is to allow pausing of asynchronous functions
|
|
44
|
+
* @kind function
|
|
45
|
+
* @name pause
|
|
46
|
+
* @param milliseconds time in milliseconds before value is resolved
|
|
47
|
+
* @param value value given
|
|
48
|
+
* @returns Promise fulfilled with given value
|
|
49
|
+
* @example <caption>Pause a function for 5000 milliseconds</caption>
|
|
50
|
+
* ```javascript
|
|
51
|
+
* import { pause } from '...';
|
|
52
|
+
* (async ()=>{
|
|
53
|
+
* console.log('hello');
|
|
54
|
+
* await pause(5000);
|
|
55
|
+
* console.log('there.');
|
|
56
|
+
* })();
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export const pause = <T = undefined>(milliseconds: number, value?: T): Promise<T> =>
|
|
60
|
+
new Promise((resolve) => setTimeout(resolve as (value?: T) => void, milliseconds, value));
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Streaming reduce for iterators -- the engine under transduceSync.
|
|
64
|
+
*
|
|
65
|
+
* Protocol (v2.1): the accumulator (`init`) is a plain array used as a
|
|
66
|
+
* *pending-emission buffer*, not a threaded iterable. The innermost step
|
|
67
|
+
* ("emit" -- see transduce.ts) pushes emitted items onto the buffer and
|
|
68
|
+
* returns it; after each step call the buffer is drained (yielded out and
|
|
69
|
+
* cleared in place). This replaces the v2.0 protocol, in which the
|
|
70
|
+
* accumulator was an iterable that each step wrapped in a fresh generator
|
|
71
|
+
* (`conjoin(init, item)`) -- that formed an ever-growing generator chain
|
|
72
|
+
* retaining ~176 bytes per item processed, i.e. unbounded heap growth on
|
|
73
|
+
* long streams.
|
|
74
|
+
*
|
|
75
|
+
* Preserved semantics:
|
|
76
|
+
* - Left-to-right composition order as written in transduceSync(...fns).
|
|
77
|
+
* - If `step` carries a `.complete(init)` method (the transducer completion
|
|
78
|
+
* protocol -- see transducers.ts's `group`/`partitionBy`), it is invoked
|
|
79
|
+
* once after the source iterator is exhausted (or halted), letting
|
|
80
|
+
* stateful transducers flush buffered state (e.g. a trailing,
|
|
81
|
+
* under-sized `group`).
|
|
82
|
+
* - HALT early termination: a step returning HALT stops consumption of
|
|
83
|
+
* the source; emissions buffered during the halting step itself are
|
|
84
|
+
* discarded (as in v2.0, where the halting step's accumulator chain was
|
|
85
|
+
* dropped), then `.complete` still flushes.
|
|
86
|
+
* @kind function
|
|
87
|
+
* @name reduceSync
|
|
88
|
+
* @param iterator source iterable
|
|
89
|
+
* @param step reducer/transducer step function
|
|
90
|
+
* @param init pending-emission buffer (defaults to a fresh array)
|
|
91
|
+
* @param ignore_halt ignore when HALT is passed
|
|
92
|
+
* @returns generator yielding each emitted item
|
|
93
|
+
*/
|
|
94
|
+
export const reduceSync = function* <In, Out = unknown>(
|
|
95
|
+
iterator: Iterable<In>,
|
|
96
|
+
step: ReducerStep<In>,
|
|
97
|
+
init: unknown[] = [],
|
|
98
|
+
ignore_halt = false,
|
|
99
|
+
): Generator<Out> {
|
|
100
|
+
for (const item of iterator) {
|
|
101
|
+
const next = step(init, item, iterator);
|
|
102
|
+
if (!ignore_halt && next === HALT) {
|
|
103
|
+
init.length = 0;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
init = next as unknown[];
|
|
107
|
+
while (init.length > 0) {
|
|
108
|
+
yield init.shift() as Out;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (step.complete) {
|
|
112
|
+
init = step.complete(init);
|
|
113
|
+
while (init.length > 0) {
|
|
114
|
+
yield init.shift() as Out;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Streaming reduce for asynchronous iterators -- the engine under
|
|
121
|
+
* transduceAsync. See reduceSync for the pending-emission-buffer protocol
|
|
122
|
+
* (v2.1), the `.complete` flush protocol, and HALT semantics -- all
|
|
123
|
+
* identical here, just async.
|
|
124
|
+
* @kind function
|
|
125
|
+
* @name reduceAsync
|
|
126
|
+
* @param iterator source (async) iterable
|
|
127
|
+
* @param step reducer/transducer step function
|
|
128
|
+
* @param init pending-emission buffer (defaults to a fresh array)
|
|
129
|
+
* @param ignore_halt ignore when HALT is passed
|
|
130
|
+
* @returns async generator yielding each emitted item
|
|
131
|
+
*/
|
|
132
|
+
export const reduceAsync = async function* <In, Out = unknown>(
|
|
133
|
+
iterator: AsyncIterable<In> | Iterable<In>,
|
|
134
|
+
step: ReducerStep<In>,
|
|
135
|
+
init: unknown[] = [],
|
|
136
|
+
ignore_halt = false,
|
|
137
|
+
): AsyncGenerator<Out> {
|
|
138
|
+
for await (const item of iterator) {
|
|
139
|
+
const next = step(init, item, iterator);
|
|
140
|
+
if (!ignore_halt && next === HALT) {
|
|
141
|
+
init.length = 0;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
init = next as unknown[];
|
|
145
|
+
while (init.length > 0) {
|
|
146
|
+
yield init.shift() as Out;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (step.complete) {
|
|
150
|
+
init = step.complete(init);
|
|
151
|
+
while (init.length > 0) {
|
|
152
|
+
yield init.shift() as Out;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Concatinates sequence of synchronous iterables
|
|
159
|
+
* @kind function
|
|
160
|
+
* @name concatSync
|
|
161
|
+
* @param iterators iterators
|
|
162
|
+
* @returns iterator generating sequence of combined from given iterables; empty iterator if nothing is passed
|
|
163
|
+
*/
|
|
164
|
+
export const concatSync = function* <T>(...iterators: Array<Iterable<T>>): Generator<T> {
|
|
165
|
+
for (const iterator of iterators) {
|
|
166
|
+
yield* iterator;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Appends items to synchronous iterator
|
|
172
|
+
* @kind function
|
|
173
|
+
* @name conjoinSync
|
|
174
|
+
* @param iterator iterator
|
|
175
|
+
* @param itemList items to be appended
|
|
176
|
+
* @returns copy of initial iterator with items appended
|
|
177
|
+
*/
|
|
178
|
+
export const conjoinSync = function* <T>(iterator?: Iterable<T>, ...itemList: T[]): Generator<T> {
|
|
179
|
+
if (iterator) {
|
|
180
|
+
yield* iterator;
|
|
181
|
+
}
|
|
182
|
+
yield* itemList;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Concatinates sequence of asynchronous iterables
|
|
187
|
+
* @kind function
|
|
188
|
+
* @name concatAsync
|
|
189
|
+
* @param iterators iterators
|
|
190
|
+
* @returns iterator generating sequence of combined from given iterables; empty iterator if nothing is passed
|
|
191
|
+
*/
|
|
192
|
+
export const concatAsync = async function* <T>(...iterators: Array<AsyncIterable<T> | Iterable<T>>): AsyncGenerator<T> {
|
|
193
|
+
for (const iterator of iterators) {
|
|
194
|
+
yield* iterator;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Appends items to asynchronous iterator
|
|
200
|
+
* @kind function
|
|
201
|
+
* @name conjoinAsync
|
|
202
|
+
* @param iterator iterator
|
|
203
|
+
* @param itemList items to be appended
|
|
204
|
+
* @returns copy of initial iterator with items appended
|
|
205
|
+
*/
|
|
206
|
+
export const conjoinAsync = async function* <T>(
|
|
207
|
+
iterator?: AsyncIterable<T> | Iterable<T>,
|
|
208
|
+
...itemList: T[]
|
|
209
|
+
): AsyncGenerator<T> {
|
|
210
|
+
if (iterator) {
|
|
211
|
+
yield* iterator;
|
|
212
|
+
}
|
|
213
|
+
yield* itemList;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Zips synchronous iterators
|
|
218
|
+
* @kind function
|
|
219
|
+
* @name zipSync
|
|
220
|
+
* @param iteratorList iterators
|
|
221
|
+
* @returns an iterator who's members are the members of the given iterators zipped sequencially
|
|
222
|
+
*/
|
|
223
|
+
export const zipSync = function* <T>(...iteratorList: Array<Iterable<T>>): Generator<T[]> {
|
|
224
|
+
const generators = iteratorList.map((iterator) => iterator[Symbol.iterator]());
|
|
225
|
+
outer: while (true) {
|
|
226
|
+
const result: T[] = [];
|
|
227
|
+
for (const generator of generators) {
|
|
228
|
+
const { value, done } = generator.next();
|
|
229
|
+
if (done) {
|
|
230
|
+
break outer;
|
|
231
|
+
}
|
|
232
|
+
result.push(value);
|
|
233
|
+
}
|
|
234
|
+
yield result;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Zips asynchronous iterators (dual of zipSync).
|
|
240
|
+
* Pulls the next value from every input in parallel each round (rather than
|
|
241
|
+
* sequentially awaiting one at a time), stopping as soon as any input is
|
|
242
|
+
* exhausted -- matching zipSync's stop-at-shortest semantics.
|
|
243
|
+
* @kind function
|
|
244
|
+
* @name zipAsync
|
|
245
|
+
* @param iteratorList (async or sync) iterators
|
|
246
|
+
* @returns an async iterator who's members are the members of the given iterators zipped sequencially
|
|
247
|
+
*/
|
|
248
|
+
export const zipAsync = async function* <T>(
|
|
249
|
+
...iteratorList: Array<AsyncIterable<T> | Iterable<T>>
|
|
250
|
+
): AsyncGenerator<T[]> {
|
|
251
|
+
const generators = iteratorList.map((iterator) =>
|
|
252
|
+
isAsyncIterator(iterator)
|
|
253
|
+
? (iterator as AsyncIterable<T>)[Symbol.asyncIterator]()
|
|
254
|
+
: (iterator as Iterable<T>)[Symbol.iterator](),
|
|
255
|
+
);
|
|
256
|
+
while (true) {
|
|
257
|
+
const results = await Promise.all(generators.map((g) => g.next()));
|
|
258
|
+
if (results.some(({ done }) => done)) {
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
yield results.map(({ value }) => value as T);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* "run" iterator as a program
|
|
267
|
+
* @kind function
|
|
268
|
+
* @name run
|
|
269
|
+
* @param program iterator
|
|
270
|
+
* @param render function to render output from iterator
|
|
271
|
+
*/
|
|
272
|
+
export const run = async <T>(
|
|
273
|
+
program: AsyncIterable<T> | Iterable<T>,
|
|
274
|
+
render: (output: T) => unknown = console.log,
|
|
275
|
+
): Promise<void> => {
|
|
276
|
+
for await (const output of program) {
|
|
277
|
+
await render(output);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export const syncFrom = function* <T>(...stuff: T[]): Generator<T> {
|
|
282
|
+
for (const thing of stuff) {
|
|
283
|
+
yield thing;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
export const asyncFrom = async function* <T>(...stuff: T[]): AsyncGenerator<T> {
|
|
287
|
+
for (const thing of stuff) {
|
|
288
|
+
yield thing;
|
|
289
|
+
}
|
|
290
|
+
};
|