@effectionx/stream-helpers 0.7.2 → 0.7.3
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/README.md +83 -0
- package/batch.ts +0 -1
- package/dist/batch.d.ts.map +1 -1
- package/dist/batch.js +0 -1
- package/dist/mod.d.ts +3 -0
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +3 -0
- package/dist/subject.d.ts.map +1 -1
- package/dist/subject.js +0 -1
- package/dist/take-until.d.ts +46 -0
- package/dist/take-until.d.ts.map +1 -0
- package/dist/take-until.js +68 -0
- package/dist/take-while.d.ts +41 -0
- package/dist/take-while.d.ts.map +1 -0
- package/dist/take-while.js +64 -0
- package/dist/take.d.ts +36 -0
- package/dist/take.d.ts.map +1 -0
- package/dist/take.js +59 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/mod.ts +3 -0
- package/package.json +1 -1
- package/subject.ts +0 -1
- package/take-until.test.ts +113 -0
- package/take-until.ts +76 -0
- package/take-while.test.ts +89 -0
- package/take-while.ts +74 -0
- package/take.test.ts +83 -0
- package/take.ts +67 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it } from "@effectionx/bdd";
|
|
2
|
+
import { expect } from "expect";
|
|
3
|
+
import { pipe } from "remeda";
|
|
4
|
+
|
|
5
|
+
import { forEach } from "./for-each.ts";
|
|
6
|
+
import { streamOf } from "./stream-of.ts";
|
|
7
|
+
import { takeWhile } from "./take-while.ts";
|
|
8
|
+
|
|
9
|
+
describe("takeWhile", () => {
|
|
10
|
+
it("should yield values while predicate is true", function* () {
|
|
11
|
+
const values: number[] = [];
|
|
12
|
+
|
|
13
|
+
const closeValue = yield* forEach(
|
|
14
|
+
function* (value) {
|
|
15
|
+
values.push(value);
|
|
16
|
+
},
|
|
17
|
+
takeWhile((x: number) => x < 3)(streamOf([1, 2, 3, 4, 5])),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect(values).toEqual([1, 2]);
|
|
21
|
+
expect(closeValue).toBe(undefined);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return source close value if stream ends before predicate fails", function* () {
|
|
25
|
+
const values: number[] = [];
|
|
26
|
+
|
|
27
|
+
const stream = streamOf(
|
|
28
|
+
(function* () {
|
|
29
|
+
yield 1;
|
|
30
|
+
yield 2;
|
|
31
|
+
return "early-close";
|
|
32
|
+
})(),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const closeValue = yield* forEach(
|
|
36
|
+
function* (value) {
|
|
37
|
+
values.push(value);
|
|
38
|
+
},
|
|
39
|
+
takeWhile((x: number) => x < 10)(stream),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
expect(values).toEqual([1, 2]);
|
|
43
|
+
expect(closeValue).toBe("early-close");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should not include the failing value", function* () {
|
|
47
|
+
const values: number[] = [];
|
|
48
|
+
|
|
49
|
+
const closeValue = yield* forEach(
|
|
50
|
+
function* (value) {
|
|
51
|
+
values.push(value);
|
|
52
|
+
},
|
|
53
|
+
takeWhile((x: number) => x < 50)(streamOf([1, 2, 100, 3])),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(values).toEqual([1, 2]);
|
|
57
|
+
expect(closeValue).toBe(undefined);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should stop immediately if first value fails predicate", function* () {
|
|
61
|
+
const values: number[] = [];
|
|
62
|
+
|
|
63
|
+
const closeValue = yield* forEach(
|
|
64
|
+
function* (value) {
|
|
65
|
+
values.push(value);
|
|
66
|
+
},
|
|
67
|
+
takeWhile((x: number) => x < 50)(streamOf([100, 1, 2])),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
expect(values).toEqual([]);
|
|
71
|
+
expect(closeValue).toBe(undefined);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should work with pipe", function* () {
|
|
75
|
+
const values: number[] = [];
|
|
76
|
+
|
|
77
|
+
const stream = pipe(
|
|
78
|
+
streamOf([1, 2, 3, 4, 5]),
|
|
79
|
+
takeWhile((x) => x < 4),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const closeValue = yield* forEach(function* (value) {
|
|
83
|
+
values.push(value);
|
|
84
|
+
}, stream);
|
|
85
|
+
|
|
86
|
+
expect(values).toEqual([1, 2, 3]);
|
|
87
|
+
expect(closeValue).toBe(undefined);
|
|
88
|
+
});
|
|
89
|
+
});
|
package/take-while.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Stream } from "effection";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a stream transformer that yields values from the source stream
|
|
5
|
+
* while the predicate returns true. Closes when the predicate returns false,
|
|
6
|
+
* without including the failing value.
|
|
7
|
+
*
|
|
8
|
+
* When the predicate fails, the stream closes immediately without the failing
|
|
9
|
+
* value. The close value will be `undefined` since we don't have access to
|
|
10
|
+
* the source's close value without draining the entire stream.
|
|
11
|
+
*
|
|
12
|
+
* If the source stream closes before the predicate returns false, the
|
|
13
|
+
* resulting stream closes with the source's close value.
|
|
14
|
+
*
|
|
15
|
+
* @template T - The type of items in the stream
|
|
16
|
+
* @template TClose - The type of the close value
|
|
17
|
+
* @param predicate - A function that returns true to continue taking values
|
|
18
|
+
* @returns A stream transformer that yields values while predicate is true
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { takeWhile, streamOf } from "@effectionx/stream-helpers";
|
|
23
|
+
*
|
|
24
|
+
* const stream = streamOf([1, 2, 3, 4, 5]);
|
|
25
|
+
* // yields 1, 2 (stops when value >= 3)
|
|
26
|
+
* const limited = takeWhile((x: number) => x < 3)(stream);
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { takeWhile, map } from "@effectionx/stream-helpers";
|
|
32
|
+
* import { pipe } from "remeda";
|
|
33
|
+
*
|
|
34
|
+
* const limited = pipe(
|
|
35
|
+
* source,
|
|
36
|
+
* map(function* (x) { return x * 2; }),
|
|
37
|
+
* takeWhile((x) => x < 100),
|
|
38
|
+
* );
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function takeWhile<T>(
|
|
42
|
+
predicate: (item: T) => boolean,
|
|
43
|
+
): <TClose>(stream: Stream<T, TClose>) => Stream<T, TClose | undefined> {
|
|
44
|
+
return <TClose>(
|
|
45
|
+
stream: Stream<T, TClose>,
|
|
46
|
+
): Stream<T, TClose | undefined> => ({
|
|
47
|
+
*[Symbol.iterator]() {
|
|
48
|
+
const subscription = yield* stream;
|
|
49
|
+
let done = false;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
*next() {
|
|
53
|
+
if (done) {
|
|
54
|
+
return { done: true, value: undefined as TClose | undefined };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const result = yield* subscription.next();
|
|
58
|
+
if (result.done) {
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!predicate(result.value)) {
|
|
63
|
+
done = true;
|
|
64
|
+
// Close immediately without the failing value
|
|
65
|
+
// We return undefined as we don't drain the stream
|
|
66
|
+
return { done: true, value: undefined as TClose | undefined };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return result;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
package/take.test.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it } from "@effectionx/bdd";
|
|
2
|
+
import { expect } from "expect";
|
|
3
|
+
import { pipe } from "remeda";
|
|
4
|
+
|
|
5
|
+
import { forEach } from "./for-each.ts";
|
|
6
|
+
import { streamOf } from "./stream-of.ts";
|
|
7
|
+
import { take } from "./take.ts";
|
|
8
|
+
|
|
9
|
+
describe("take", () => {
|
|
10
|
+
it("should take first n values and close with the nth value", function* () {
|
|
11
|
+
const values: number[] = [];
|
|
12
|
+
|
|
13
|
+
const closeValue = yield* forEach(
|
|
14
|
+
function* (value) {
|
|
15
|
+
values.push(value);
|
|
16
|
+
},
|
|
17
|
+
take<number>(3)(streamOf([1, 2, 3, 4, 5])),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect(values).toEqual([1, 2]);
|
|
21
|
+
expect(closeValue).toBe(3);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return source close value if stream ends before n values", function* () {
|
|
25
|
+
const values: number[] = [];
|
|
26
|
+
|
|
27
|
+
const stream = streamOf(
|
|
28
|
+
(function* () {
|
|
29
|
+
yield 1;
|
|
30
|
+
yield 2;
|
|
31
|
+
return "early-close";
|
|
32
|
+
})(),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const closeValue = yield* forEach(function* (value) {
|
|
36
|
+
values.push(value);
|
|
37
|
+
}, take<number>(5)(stream));
|
|
38
|
+
|
|
39
|
+
expect(values).toEqual([1, 2]);
|
|
40
|
+
expect(closeValue).toBe("early-close");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should work with n=1", function* () {
|
|
44
|
+
const values: number[] = [];
|
|
45
|
+
|
|
46
|
+
const closeValue = yield* forEach(
|
|
47
|
+
function* (value) {
|
|
48
|
+
values.push(value);
|
|
49
|
+
},
|
|
50
|
+
take<number>(1)(streamOf([42, 100])),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(values).toEqual([]);
|
|
54
|
+
expect(closeValue).toBe(42);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should work with n=0", function* () {
|
|
58
|
+
const values: number[] = [];
|
|
59
|
+
|
|
60
|
+
const closeValue = yield* forEach(
|
|
61
|
+
function* (value) {
|
|
62
|
+
values.push(value);
|
|
63
|
+
},
|
|
64
|
+
take<number>(0)(streamOf([1, 2, 3])),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(values).toEqual([]);
|
|
68
|
+
expect(closeValue).toBe(undefined);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should work with pipe", function* () {
|
|
72
|
+
const values: number[] = [];
|
|
73
|
+
|
|
74
|
+
const stream = pipe(streamOf([1, 2, 3, 4, 5]), take(2));
|
|
75
|
+
|
|
76
|
+
const closeValue = yield* forEach(function* (value) {
|
|
77
|
+
values.push(value);
|
|
78
|
+
}, stream);
|
|
79
|
+
|
|
80
|
+
expect(values).toEqual([1]);
|
|
81
|
+
expect(closeValue).toBe(2);
|
|
82
|
+
});
|
|
83
|
+
});
|
package/take.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Stream } from "effection";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a stream transformer that yields the first `n` values from the
|
|
5
|
+
* source stream, then closes with the last taken value.
|
|
6
|
+
*
|
|
7
|
+
* If the source stream closes before yielding `n` values, the resulting
|
|
8
|
+
* stream closes with the source's close value.
|
|
9
|
+
*
|
|
10
|
+
* @template T - The type of items in the stream
|
|
11
|
+
* @template TClose - The type of the close value
|
|
12
|
+
* @param n - The number of values to take
|
|
13
|
+
* @returns A stream transformer that yields at most `n` values
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { take, streamOf } from "@effectionx/stream-helpers";
|
|
18
|
+
*
|
|
19
|
+
* const stream = streamOf([1, 2, 3, 4, 5]);
|
|
20
|
+
* // yields 1, 2, then closes with 3
|
|
21
|
+
* const limited = take(3)(stream);
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { take, map } from "@effectionx/stream-helpers";
|
|
27
|
+
* import { pipe } from "remeda";
|
|
28
|
+
*
|
|
29
|
+
* const limited = pipe(
|
|
30
|
+
* source,
|
|
31
|
+
* map(function* (x) { return x * 2; }),
|
|
32
|
+
* take(5),
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function take<T>(
|
|
37
|
+
n: number,
|
|
38
|
+
): <TClose>(stream: Stream<T, TClose>) => Stream<T, T | TClose> {
|
|
39
|
+
return <TClose>(stream: Stream<T, TClose>): Stream<T, T | TClose> => ({
|
|
40
|
+
*[Symbol.iterator]() {
|
|
41
|
+
const subscription = yield* stream;
|
|
42
|
+
let count = 0;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
*next() {
|
|
46
|
+
if (count >= n) {
|
|
47
|
+
// Should not happen if used correctly, but handle gracefully
|
|
48
|
+
return { done: true, value: undefined as unknown as T | TClose };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = yield* subscription.next();
|
|
52
|
+
if (result.done) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
count++;
|
|
57
|
+
if (count >= n) {
|
|
58
|
+
// This is the nth value, return it and mark as done
|
|
59
|
+
return { done: true, value: result.value };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|