@mathscapes/iterflow 1.0.0-rc1

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.
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Base error class for all iterflow errors.
3
+ */
4
+ declare class IterflowError extends Error {
5
+ constructor(msg: string);
6
+ }
7
+ /**
8
+ * Thrown by statistical methods when called on empty sequences.
9
+ */
10
+ declare class EmptySequenceError extends IterflowError {
11
+ readonly op: string;
12
+ constructor(op: string);
13
+ }
14
+ type Predicate<T> = (v: T, i: number) => boolean;
15
+ type Mapper<T, U> = (v: T, i: number) => U;
16
+ type Reducer<T, U> = (acc: U, v: T, i: number) => U;
17
+ type FlatMapper<T, U> = (v: T, i: number) => Iterable<U>;
18
+ /**
19
+ * Type guard to check if a value is iterable.
20
+ */
21
+ declare const isIterable: (v: unknown) => v is Iterable<unknown>;
22
+
23
+ /**
24
+ * Calculate the sum of all numbers.
25
+ * @throws {EmptySequenceError} If the sequence is empty.
26
+ */
27
+ declare const sum: (src: Iterable<number>) => number;
28
+ /**
29
+ * Calculate the arithmetic mean.
30
+ * @throws {EmptySequenceError} If the sequence is empty.
31
+ */
32
+ declare const mean: (src: Iterable<number>) => number;
33
+ /**
34
+ * Calculate the median (50th percentile) using Quickselect algorithm.
35
+ * @throws {EmptySequenceError} If the sequence is empty.
36
+ */
37
+ declare const median: (src: Iterable<number>) => number;
38
+ /**
39
+ * Find the minimum value.
40
+ * @throws {EmptySequenceError} If the sequence is empty.
41
+ */
42
+ declare const min: (src: Iterable<number>) => number;
43
+ /**
44
+ * Find the maximum value.
45
+ * @throws {EmptySequenceError} If the sequence is empty.
46
+ */
47
+ declare const max: (src: Iterable<number>) => number;
48
+ /**
49
+ * Calculate the population variance using Welford's online algorithm.
50
+ * @throws {EmptySequenceError} If the sequence is empty.
51
+ */
52
+ declare const variance: (src: Iterable<number>) => number;
53
+
54
+ /**
55
+ * iterflow - Lazy iterators with statistics and windowing
56
+ * @packageDocumentation
57
+ */
58
+
59
+ declare class Iterflow<T> implements Iterable<T> {
60
+ private readonly src;
61
+ constructor(src: Iterable<T>);
62
+ [Symbol.iterator](): Iterator<T>;
63
+ map<U>(fn: Mapper<T, U>): Iterflow<U>;
64
+ filter(fn: Predicate<T>): Iterflow<T>;
65
+ flatMap<U>(fn: FlatMapper<T, U>): Iterflow<U>;
66
+ take(n: number): Iterflow<T>;
67
+ drop(n: number): Iterflow<T>;
68
+ takeWhile(fn: Predicate<T>): Iterflow<T>;
69
+ dropWhile(fn: Predicate<T>): Iterflow<T>;
70
+ distinct(): Iterflow<T>;
71
+ /**
72
+ * Yield [index, value] pairs.
73
+ */
74
+ enumerate(): Iterflow<[number, T]>;
75
+ /**
76
+ * Append additional iterables to the sequence.
77
+ */
78
+ concat<U>(...others: Iterable<U>[]): Iterflow<T | U>;
79
+ /**
80
+ * Create sliding windows of the specified size.
81
+ */
82
+ window(size: number): Iterflow<T[]>;
83
+ /**
84
+ * Split the sequence into fixed-size chunks.
85
+ */
86
+ chunk(size: number): Iterflow<T[]>;
87
+ toArray(): T[];
88
+ reduce<U>(fn: Reducer<T, U>, init: U): U;
89
+ find(fn: Predicate<T>): T | undefined;
90
+ forEach(fn: (v: T, i: number) => void): void;
91
+ first(): T | undefined;
92
+ last(): T | undefined;
93
+ count(): number;
94
+ some(fn: Predicate<T>): boolean;
95
+ every(fn: Predicate<T>): boolean;
96
+ /**
97
+ * Calculate the sum of all numbers.
98
+ * @throws {EmptySequenceError} If the sequence is empty.
99
+ */
100
+ sum(this: Iterflow<number>): number;
101
+ /**
102
+ * Calculate the arithmetic mean.
103
+ * @throws {EmptySequenceError} If the sequence is empty.
104
+ */
105
+ mean(this: Iterflow<number>): number;
106
+ /**
107
+ * Calculate the median (50th percentile).
108
+ * @throws {EmptySequenceError} If the sequence is empty.
109
+ */
110
+ median(this: Iterflow<number>): number;
111
+ /**
112
+ * Find the minimum value.
113
+ * @throws {EmptySequenceError} If the sequence is empty.
114
+ */
115
+ min(this: Iterflow<number>): number;
116
+ /**
117
+ * Find the maximum value.
118
+ * @throws {EmptySequenceError} If the sequence is empty.
119
+ */
120
+ max(this: Iterflow<number>): number;
121
+ /**
122
+ * Calculate the population variance.
123
+ * @throws {EmptySequenceError} If the sequence is empty.
124
+ */
125
+ variance(this: Iterflow<number>): number;
126
+ }
127
+ declare function iter<T>(src: Iterable<T>): Iterflow<T>;
128
+
129
+ export { EmptySequenceError, type FlatMapper, Iterflow, IterflowError, type Mapper, type Predicate, type Reducer, isIterable, iter, max, mean, median, min, sum, variance };
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Base error class for all iterflow errors.
3
+ */
4
+ declare class IterflowError extends Error {
5
+ constructor(msg: string);
6
+ }
7
+ /**
8
+ * Thrown by statistical methods when called on empty sequences.
9
+ */
10
+ declare class EmptySequenceError extends IterflowError {
11
+ readonly op: string;
12
+ constructor(op: string);
13
+ }
14
+ type Predicate<T> = (v: T, i: number) => boolean;
15
+ type Mapper<T, U> = (v: T, i: number) => U;
16
+ type Reducer<T, U> = (acc: U, v: T, i: number) => U;
17
+ type FlatMapper<T, U> = (v: T, i: number) => Iterable<U>;
18
+ /**
19
+ * Type guard to check if a value is iterable.
20
+ */
21
+ declare const isIterable: (v: unknown) => v is Iterable<unknown>;
22
+
23
+ /**
24
+ * Calculate the sum of all numbers.
25
+ * @throws {EmptySequenceError} If the sequence is empty.
26
+ */
27
+ declare const sum: (src: Iterable<number>) => number;
28
+ /**
29
+ * Calculate the arithmetic mean.
30
+ * @throws {EmptySequenceError} If the sequence is empty.
31
+ */
32
+ declare const mean: (src: Iterable<number>) => number;
33
+ /**
34
+ * Calculate the median (50th percentile) using Quickselect algorithm.
35
+ * @throws {EmptySequenceError} If the sequence is empty.
36
+ */
37
+ declare const median: (src: Iterable<number>) => number;
38
+ /**
39
+ * Find the minimum value.
40
+ * @throws {EmptySequenceError} If the sequence is empty.
41
+ */
42
+ declare const min: (src: Iterable<number>) => number;
43
+ /**
44
+ * Find the maximum value.
45
+ * @throws {EmptySequenceError} If the sequence is empty.
46
+ */
47
+ declare const max: (src: Iterable<number>) => number;
48
+ /**
49
+ * Calculate the population variance using Welford's online algorithm.
50
+ * @throws {EmptySequenceError} If the sequence is empty.
51
+ */
52
+ declare const variance: (src: Iterable<number>) => number;
53
+
54
+ /**
55
+ * iterflow - Lazy iterators with statistics and windowing
56
+ * @packageDocumentation
57
+ */
58
+
59
+ declare class Iterflow<T> implements Iterable<T> {
60
+ private readonly src;
61
+ constructor(src: Iterable<T>);
62
+ [Symbol.iterator](): Iterator<T>;
63
+ map<U>(fn: Mapper<T, U>): Iterflow<U>;
64
+ filter(fn: Predicate<T>): Iterflow<T>;
65
+ flatMap<U>(fn: FlatMapper<T, U>): Iterflow<U>;
66
+ take(n: number): Iterflow<T>;
67
+ drop(n: number): Iterflow<T>;
68
+ takeWhile(fn: Predicate<T>): Iterflow<T>;
69
+ dropWhile(fn: Predicate<T>): Iterflow<T>;
70
+ distinct(): Iterflow<T>;
71
+ /**
72
+ * Yield [index, value] pairs.
73
+ */
74
+ enumerate(): Iterflow<[number, T]>;
75
+ /**
76
+ * Append additional iterables to the sequence.
77
+ */
78
+ concat<U>(...others: Iterable<U>[]): Iterflow<T | U>;
79
+ /**
80
+ * Create sliding windows of the specified size.
81
+ */
82
+ window(size: number): Iterflow<T[]>;
83
+ /**
84
+ * Split the sequence into fixed-size chunks.
85
+ */
86
+ chunk(size: number): Iterflow<T[]>;
87
+ toArray(): T[];
88
+ reduce<U>(fn: Reducer<T, U>, init: U): U;
89
+ find(fn: Predicate<T>): T | undefined;
90
+ forEach(fn: (v: T, i: number) => void): void;
91
+ first(): T | undefined;
92
+ last(): T | undefined;
93
+ count(): number;
94
+ some(fn: Predicate<T>): boolean;
95
+ every(fn: Predicate<T>): boolean;
96
+ /**
97
+ * Calculate the sum of all numbers.
98
+ * @throws {EmptySequenceError} If the sequence is empty.
99
+ */
100
+ sum(this: Iterflow<number>): number;
101
+ /**
102
+ * Calculate the arithmetic mean.
103
+ * @throws {EmptySequenceError} If the sequence is empty.
104
+ */
105
+ mean(this: Iterflow<number>): number;
106
+ /**
107
+ * Calculate the median (50th percentile).
108
+ * @throws {EmptySequenceError} If the sequence is empty.
109
+ */
110
+ median(this: Iterflow<number>): number;
111
+ /**
112
+ * Find the minimum value.
113
+ * @throws {EmptySequenceError} If the sequence is empty.
114
+ */
115
+ min(this: Iterflow<number>): number;
116
+ /**
117
+ * Find the maximum value.
118
+ * @throws {EmptySequenceError} If the sequence is empty.
119
+ */
120
+ max(this: Iterflow<number>): number;
121
+ /**
122
+ * Calculate the population variance.
123
+ * @throws {EmptySequenceError} If the sequence is empty.
124
+ */
125
+ variance(this: Iterflow<number>): number;
126
+ }
127
+ declare function iter<T>(src: Iterable<T>): Iterflow<T>;
128
+
129
+ export { EmptySequenceError, type FlatMapper, Iterflow, IterflowError, type Mapper, type Predicate, type Reducer, isIterable, iter, max, mean, median, min, sum, variance };
package/dist/index.js ADDED
@@ -0,0 +1,441 @@
1
+ // src/core.ts
2
+ var IterflowError = class extends Error {
3
+ constructor(msg) {
4
+ super(msg);
5
+ this.name = "IterflowError";
6
+ }
7
+ };
8
+ var EmptySequenceError = class extends IterflowError {
9
+ constructor(op) {
10
+ super(`Cannot compute ${op} of empty sequence`);
11
+ this.op = op;
12
+ this.name = "EmptySequenceError";
13
+ }
14
+ };
15
+ var isIterable = (v) => v != null && typeof v === "object" && Symbol.iterator in v;
16
+
17
+ // src/internal.ts
18
+ function makeTransform(src, generator) {
19
+ return {
20
+ *[Symbol.iterator]() {
21
+ yield* generator(src);
22
+ }
23
+ };
24
+ }
25
+ function assertNonEmpty(count2, op) {
26
+ if (count2 === 0) {
27
+ throw new EmptySequenceError(op);
28
+ }
29
+ }
30
+ function assertHasValue(has, op) {
31
+ if (!has) {
32
+ throw new EmptySequenceError(op);
33
+ }
34
+ }
35
+ function validateInteger(n, param) {
36
+ if (!Number.isFinite(n)) {
37
+ throw new RangeError(`${param} must be finite`);
38
+ }
39
+ if (!Number.isInteger(n)) {
40
+ throw new TypeError(`${param} must be an integer`);
41
+ }
42
+ }
43
+ function validateNonNegative(n, param) {
44
+ validateInteger(n, param);
45
+ if (n < 0) {
46
+ throw new RangeError(`${param} must be non-negative`);
47
+ }
48
+ }
49
+ function validatePositive(n, param) {
50
+ validateInteger(n, param);
51
+ if (n <= 0) {
52
+ throw new RangeError(`${param} must be positive`);
53
+ }
54
+ }
55
+
56
+ // src/transforms.ts
57
+ function map(src, fn) {
58
+ return makeTransform(src, function* (s) {
59
+ let i = 0;
60
+ for (const v of s) yield fn(v, i++);
61
+ });
62
+ }
63
+ function flatMap(src, fn) {
64
+ return makeTransform(src, function* (s) {
65
+ let i = 0;
66
+ for (const v of s) yield* fn(v, i++);
67
+ });
68
+ }
69
+ function enumerate(src) {
70
+ return makeTransform(src, function* (s) {
71
+ let i = 0;
72
+ for (const v of s) yield [i++, v];
73
+ });
74
+ }
75
+ function filter(src, fn) {
76
+ return makeTransform(src, function* (s) {
77
+ let i = 0;
78
+ for (const v of s) if (fn(v, i++)) yield v;
79
+ });
80
+ }
81
+ function take(src, n) {
82
+ validateNonNegative(n, "Count");
83
+ return makeTransform(src, function* (s) {
84
+ let remaining = n;
85
+ for (const v of s) {
86
+ if (remaining-- <= 0) break;
87
+ yield v;
88
+ }
89
+ });
90
+ }
91
+ function drop(src, n) {
92
+ validateNonNegative(n, "Count");
93
+ return makeTransform(src, function* (s) {
94
+ let i = 0;
95
+ for (const v of s) if (i++ >= n) yield v;
96
+ });
97
+ }
98
+ function takeWhile(src, fn) {
99
+ return makeTransform(src, function* (s) {
100
+ let i = 0;
101
+ for (const v of s) {
102
+ if (!fn(v, i++)) break;
103
+ yield v;
104
+ }
105
+ });
106
+ }
107
+ function dropWhile(src, fn) {
108
+ return makeTransform(src, function* (s) {
109
+ let i = 0, dropping = true;
110
+ for (const v of s) {
111
+ if (dropping && !fn(v, i)) dropping = false;
112
+ if (!dropping) yield v;
113
+ i++;
114
+ }
115
+ });
116
+ }
117
+ function distinct(src) {
118
+ return makeTransform(src, function* (s) {
119
+ const seen = /* @__PURE__ */ new Set();
120
+ for (const v of s) {
121
+ if (!seen.has(v)) {
122
+ seen.add(v);
123
+ yield v;
124
+ }
125
+ }
126
+ });
127
+ }
128
+ function window(src, size) {
129
+ validatePositive(size, "Window size");
130
+ return makeTransform(src, function* (s) {
131
+ const buf = [];
132
+ for (const v of s) {
133
+ buf.push(v);
134
+ if (buf.length === size) {
135
+ yield buf.slice();
136
+ buf.shift();
137
+ }
138
+ }
139
+ });
140
+ }
141
+ function chunk(src, size) {
142
+ validatePositive(size, "Chunk size");
143
+ return makeTransform(src, function* (s) {
144
+ let batch = [];
145
+ for (const v of s) {
146
+ batch.push(v);
147
+ if (batch.length === size) {
148
+ yield batch;
149
+ batch = [];
150
+ }
151
+ }
152
+ if (batch.length) yield batch;
153
+ });
154
+ }
155
+ function concat(src, ...others) {
156
+ return makeTransform(src, function* (s) {
157
+ yield* s;
158
+ for (const o of others) yield* o;
159
+ });
160
+ }
161
+
162
+ // src/terminals.ts
163
+ function toArray(src) {
164
+ return Array.from(src);
165
+ }
166
+ function reduce(src, fn, init) {
167
+ let acc = init, i = 0;
168
+ for (const v of src) acc = fn(acc, v, i++);
169
+ return acc;
170
+ }
171
+ function find(src, fn) {
172
+ let i = 0;
173
+ for (const v of src) if (fn(v, i++)) return v;
174
+ return void 0;
175
+ }
176
+ function forEach(src, fn) {
177
+ let i = 0;
178
+ for (const v of src) fn(v, i++);
179
+ }
180
+ function first(src) {
181
+ for (const v of src) return v;
182
+ return void 0;
183
+ }
184
+ function last(src) {
185
+ if (Array.isArray(src)) return src[src.length - 1];
186
+ let last2;
187
+ for (const v of src) last2 = v;
188
+ return last2;
189
+ }
190
+ function count(src) {
191
+ if (Array.isArray(src)) return src.length;
192
+ let n = 0;
193
+ for (const _ of src) n++;
194
+ return n;
195
+ }
196
+ function some(src, fn) {
197
+ let i = 0;
198
+ for (const v of src) if (fn(v, i++)) return true;
199
+ return false;
200
+ }
201
+ function every(src, fn) {
202
+ let i = 0;
203
+ for (const v of src) if (!fn(v, i++)) return false;
204
+ return true;
205
+ }
206
+ var sum = (src) => {
207
+ let total = 0, count2 = 0;
208
+ for (const v of src) {
209
+ total += v;
210
+ count2++;
211
+ }
212
+ assertNonEmpty(count2, "sum");
213
+ return total;
214
+ };
215
+ var mean = (src) => {
216
+ let total = 0, count2 = 0;
217
+ for (const v of src) {
218
+ total += v;
219
+ count2++;
220
+ }
221
+ assertNonEmpty(count2, "mean");
222
+ return total / count2;
223
+ };
224
+ function partition(arr, left, right) {
225
+ const pivot = arr[right];
226
+ let i = left;
227
+ for (let j = left; j < right; j++) {
228
+ if (arr[j] < pivot) {
229
+ [arr[i], arr[j]] = [arr[j], arr[i]];
230
+ i++;
231
+ }
232
+ }
233
+ [arr[i], arr[right]] = [arr[right], arr[i]];
234
+ return i;
235
+ }
236
+ function quickselect(arr, k) {
237
+ let left = 0;
238
+ let right = arr.length - 1;
239
+ while (left < right) {
240
+ const pivotIndex = partition(arr, left, right);
241
+ if (pivotIndex === k) {
242
+ return arr[k];
243
+ } else if (pivotIndex > k) {
244
+ right = pivotIndex - 1;
245
+ } else {
246
+ left = pivotIndex + 1;
247
+ }
248
+ }
249
+ return arr[left];
250
+ }
251
+ var median = (src) => {
252
+ const arr = Array.from(src);
253
+ assertNonEmpty(arr.length, "median");
254
+ const mid = arr.length >> 1;
255
+ if (arr.length % 2) {
256
+ return quickselect(arr, mid);
257
+ } else {
258
+ const upper = quickselect(arr, mid);
259
+ const lower = quickselect(arr, mid - 1);
260
+ return (lower + upper) / 2;
261
+ }
262
+ };
263
+ var min = (src) => {
264
+ let result = Infinity, has = false;
265
+ for (const v of src) {
266
+ if (v < result) result = v;
267
+ has = true;
268
+ }
269
+ assertHasValue(has, "min");
270
+ return result;
271
+ };
272
+ var max = (src) => {
273
+ let result = -Infinity, has = false;
274
+ for (const v of src) {
275
+ if (v > result) result = v;
276
+ has = true;
277
+ }
278
+ assertHasValue(has, "max");
279
+ return result;
280
+ };
281
+ var variance = (src) => {
282
+ let count2 = 0;
283
+ let mean2 = 0;
284
+ let M2 = 0;
285
+ for (const x of src) {
286
+ count2++;
287
+ const delta = x - mean2;
288
+ mean2 += delta / count2;
289
+ const delta2 = x - mean2;
290
+ M2 += delta * delta2;
291
+ }
292
+ assertNonEmpty(count2, "variance");
293
+ return M2 / count2;
294
+ };
295
+
296
+ // src/index.ts
297
+ var Iterflow = class _Iterflow {
298
+ constructor(src) {
299
+ this.src = src;
300
+ }
301
+ [Symbol.iterator]() {
302
+ return this.src[Symbol.iterator]();
303
+ }
304
+ // Transforms
305
+ map(fn) {
306
+ return new _Iterflow(map(this.src, fn));
307
+ }
308
+ filter(fn) {
309
+ return new _Iterflow(filter(this.src, fn));
310
+ }
311
+ flatMap(fn) {
312
+ return new _Iterflow(flatMap(this.src, fn));
313
+ }
314
+ take(n) {
315
+ return new _Iterflow(take(this.src, n));
316
+ }
317
+ drop(n) {
318
+ return new _Iterflow(drop(this.src, n));
319
+ }
320
+ takeWhile(fn) {
321
+ return new _Iterflow(takeWhile(this.src, fn));
322
+ }
323
+ dropWhile(fn) {
324
+ return new _Iterflow(dropWhile(this.src, fn));
325
+ }
326
+ distinct() {
327
+ return new _Iterflow(distinct(this.src));
328
+ }
329
+ /**
330
+ * Yield [index, value] pairs.
331
+ */
332
+ enumerate() {
333
+ return new _Iterflow(enumerate(this.src));
334
+ }
335
+ /**
336
+ * Append additional iterables to the sequence.
337
+ */
338
+ concat(...others) {
339
+ return new _Iterflow(concat(this.src, ...others));
340
+ }
341
+ /**
342
+ * Create sliding windows of the specified size.
343
+ */
344
+ window(size) {
345
+ return new _Iterflow(window(this.src, size));
346
+ }
347
+ /**
348
+ * Split the sequence into fixed-size chunks.
349
+ */
350
+ chunk(size) {
351
+ return new _Iterflow(chunk(this.src, size));
352
+ }
353
+ // Terminals
354
+ toArray() {
355
+ return toArray(this.src);
356
+ }
357
+ reduce(fn, init) {
358
+ return reduce(this.src, fn, init);
359
+ }
360
+ find(fn) {
361
+ return find(this.src, fn);
362
+ }
363
+ forEach(fn) {
364
+ forEach(this.src, fn);
365
+ }
366
+ first() {
367
+ return first(this.src);
368
+ }
369
+ last() {
370
+ return last(this.src);
371
+ }
372
+ count() {
373
+ return count(this.src);
374
+ }
375
+ some(fn) {
376
+ return some(this.src, fn);
377
+ }
378
+ every(fn) {
379
+ return every(this.src, fn);
380
+ }
381
+ // Stats (numbers only)
382
+ /**
383
+ * Calculate the sum of all numbers.
384
+ * @throws {EmptySequenceError} If the sequence is empty.
385
+ */
386
+ sum() {
387
+ return sum(this.src);
388
+ }
389
+ /**
390
+ * Calculate the arithmetic mean.
391
+ * @throws {EmptySequenceError} If the sequence is empty.
392
+ */
393
+ mean() {
394
+ return mean(this.src);
395
+ }
396
+ /**
397
+ * Calculate the median (50th percentile).
398
+ * @throws {EmptySequenceError} If the sequence is empty.
399
+ */
400
+ median() {
401
+ return median(this.src);
402
+ }
403
+ /**
404
+ * Find the minimum value.
405
+ * @throws {EmptySequenceError} If the sequence is empty.
406
+ */
407
+ min() {
408
+ return min(this.src);
409
+ }
410
+ /**
411
+ * Find the maximum value.
412
+ * @throws {EmptySequenceError} If the sequence is empty.
413
+ */
414
+ max() {
415
+ return max(this.src);
416
+ }
417
+ /**
418
+ * Calculate the population variance.
419
+ * @throws {EmptySequenceError} If the sequence is empty.
420
+ */
421
+ variance() {
422
+ return variance(this.src);
423
+ }
424
+ };
425
+ function iter(src) {
426
+ return new Iterflow(src);
427
+ }
428
+ export {
429
+ EmptySequenceError,
430
+ Iterflow,
431
+ IterflowError,
432
+ isIterable,
433
+ iter,
434
+ max,
435
+ mean,
436
+ median,
437
+ min,
438
+ sum,
439
+ variance
440
+ };
441
+ //# sourceMappingURL=index.js.map