Skip to content

Commit 8206caf

Browse files
authoredApr 2, 2024··
add Iterable module (#2415)
1 parent e066ae2 commit 8206caf

File tree

5 files changed

+1492
-20
lines changed

5 files changed

+1492
-20
lines changed
 

‎.changeset/polite-panthers-jam.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
"effect": patch
3+
---
4+
5+
add Iterable module
6+
7+
This module shares many apis compared to "effect/ReadonlyArray", but is fully lazy.
8+
9+
```ts
10+
import { Iterable, pipe } from "effect";
11+
12+
// Only 5 items will be generated & transformed
13+
pipe(
14+
Iterable.range(1, 100),
15+
Iterable.map((i) => `item ${i}`),
16+
Iterable.take(5)
17+
);
18+
```

‎packages/effect/src/Iterable.ts

+1,009
Large diffs are not rendered by default.

‎packages/effect/src/ReadonlyArray.ts

+2-20
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { LazyArg } from "./Function.js"
1212
import { dual, identity } from "./Function.js"
1313
import type { TypeLambda } from "./HKT.js"
1414
import * as readonlyArray from "./internal/readonlyArray.js"
15+
import * as EffectIterable from "./Iterable.js"
1516
import type { Option } from "./Option.js"
1617
import * as O from "./Option.js"
1718
import * as Order from "./Order.js"
@@ -713,26 +714,7 @@ export const findFirst: {
713714
<A, B>(self: Iterable<A>, f: (a: A, i: number) => Option<B>): Option<B>
714715
<A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option<B>
715716
<A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option<A>
716-
} = dual(
717-
2,
718-
<A>(self: Iterable<A>, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option<A>)): Option<A> => {
719-
let i = 0
720-
for (const a of self) {
721-
const o = f(a, i)
722-
if (isBoolean(o)) {
723-
if (o) {
724-
return O.some(a)
725-
}
726-
} else {
727-
if (O.isSome(o)) {
728-
return o
729-
}
730-
}
731-
i++
732-
}
733-
return O.none()
734-
}
735-
)
717+
} = EffectIterable.findFirst
736718

737719
/**
738720
* Find the last element for which a predicate holds.

‎packages/effect/src/index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,13 @@ export * as HashSet from "./HashSet.js"
330330
*/
331331
export * as Inspectable from "./Inspectable.js"
332332

333+
/**
334+
* This module provides utility functions for working with Iterables in TypeScript.
335+
*
336+
* @since 2.0.0
337+
*/
338+
export * as Iterable from "./Iterable.js"
339+
333340
/**
334341
* @since 2.0.0
335342
*/

‎packages/effect/test/Iterable.test.ts

+456
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,456 @@
1+
import { deepStrictEqual, strictEqual } from "effect-test/util"
2+
import { pipe } from "effect/Function"
3+
import * as Iter from "effect/Iterable"
4+
import * as Number from "effect/Number"
5+
import * as O from "effect/Option"
6+
import type { Predicate } from "effect/Predicate"
7+
import { assert, describe, expect, it } from "vitest"
8+
9+
const symA = Symbol.for("a")
10+
const symB = Symbol.for("b")
11+
const symC = Symbol.for("c")
12+
13+
const toArray = <A>(i: Iterable<A>) => {
14+
if (Array.isArray(i)) {
15+
throw new Error("not an iterable")
16+
}
17+
return Array.from(i)
18+
}
19+
20+
describe("Iterable", () => {
21+
it("of", () => {
22+
expect(Array.from(Iter.of(1))).toEqual([1])
23+
})
24+
25+
describe("iterable inputs", () => {
26+
it("prepend", () => {
27+
deepStrictEqual(pipe([1, 2, 3], Iter.prepend(0), toArray), [0, 1, 2, 3])
28+
deepStrictEqual(pipe([[2]], Iter.prepend([1]), toArray), [[1], [2]])
29+
30+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.prepend(0), toArray), [0, 1, 2, 3])
31+
deepStrictEqual(pipe(new Set([[2]]), Iter.prepend([1]), toArray), [[1], [2]])
32+
})
33+
34+
it("prependAll", () => {
35+
deepStrictEqual(pipe([3, 4], Iter.prependAll([1, 2]), toArray), [1, 2, 3, 4])
36+
37+
deepStrictEqual(pipe([3, 4], Iter.prependAll(new Set([1, 2])), toArray), [1, 2, 3, 4])
38+
deepStrictEqual(pipe(new Set([3, 4]), Iter.prependAll([1, 2]), toArray), [1, 2, 3, 4])
39+
})
40+
41+
it("append", () => {
42+
deepStrictEqual(pipe([1, 2, 3], Iter.append(4), toArray), [1, 2, 3, 4])
43+
deepStrictEqual(pipe([[1]], Iter.append([2]), toArray), [[1], [2]])
44+
45+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.append(4), toArray), [1, 2, 3, 4])
46+
deepStrictEqual(pipe(new Set([[1]]), Iter.append([2]), toArray), [[1], [2]])
47+
})
48+
49+
it("appendAll", () => {
50+
deepStrictEqual(pipe([1, 2], Iter.appendAll([3, 4]), toArray), [1, 2, 3, 4])
51+
52+
deepStrictEqual(pipe([1, 2], Iter.appendAll(new Set([3, 4])), toArray), [1, 2, 3, 4])
53+
deepStrictEqual(pipe(new Set([1, 2]), Iter.appendAll([3, 4]), toArray), [1, 2, 3, 4])
54+
})
55+
56+
it("scan", () => {
57+
const f = (b: number, a: number) => b - a
58+
deepStrictEqual(pipe([1, 2, 3], Iter.scan(10, f), toArray), [10, 9, 7, 4])
59+
deepStrictEqual(pipe([0], Iter.scan(10, f), toArray), [10, 10])
60+
deepStrictEqual(pipe([], Iter.scan(10, f), toArray), [10])
61+
62+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.scan(10, f), toArray), [10, 9, 7, 4])
63+
deepStrictEqual(pipe(new Set([0]), Iter.scan(10, f), toArray), [10, 10])
64+
deepStrictEqual(pipe(new Set([]), Iter.scan(10, f), toArray), [10])
65+
})
66+
67+
it("take", () => {
68+
expect(pipe([1, 2, 3, 4], Iter.take(2), toArray)).toEqual([1, 2])
69+
expect(pipe([1, 2, 3, 4], Iter.take(0), toArray)).toEqual([])
70+
// out of bounds
71+
expect(pipe([1, 2, 3, 4], Iter.take(-10), toArray)).toEqual([])
72+
expect(pipe([1, 2, 3, 4], Iter.take(10), toArray)).toEqual([1, 2, 3, 4])
73+
74+
expect(pipe(new Set([1, 2, 3, 4]), Iter.take(2), toArray)).toEqual([1, 2])
75+
expect(pipe(new Set([1, 2, 3, 4]), Iter.take(0), toArray)).toEqual([])
76+
// out of bounds
77+
expect(pipe(new Set([1, 2, 3, 4]), Iter.take(-10), toArray)).toEqual([])
78+
expect(pipe(new Set([1, 2, 3, 4]), Iter.take(10), toArray)).toEqual([1, 2, 3, 4])
79+
})
80+
81+
it("takeWhile", () => {
82+
const f = (n: number) => n % 2 === 0
83+
deepStrictEqual(pipe([2, 4, 3, 6], Iter.takeWhile(f), toArray), [2, 4])
84+
deepStrictEqual(pipe(Iter.empty(), Iter.takeWhile(f), toArray), [])
85+
deepStrictEqual(pipe([1, 2, 4], Iter.takeWhile(f), toArray), [])
86+
deepStrictEqual(pipe([2, 4], Iter.takeWhile(f), toArray), [2, 4])
87+
88+
deepStrictEqual(pipe(new Set([2, 4, 3, 6]), Iter.takeWhile(f), toArray), [2, 4])
89+
deepStrictEqual(pipe(new Set<number>(), Iter.takeWhile(f), toArray), [])
90+
deepStrictEqual(pipe(new Set([1, 2, 4]), Iter.takeWhile(f), toArray), [])
91+
deepStrictEqual(pipe(new Set([2, 4]), Iter.takeWhile(f), toArray), [2, 4])
92+
})
93+
94+
it("drop", () => {
95+
deepStrictEqual(pipe(Iter.empty(), Iter.drop(0), toArray), [])
96+
deepStrictEqual(pipe([1, 2], Iter.drop(0), toArray), [1, 2])
97+
deepStrictEqual(pipe([1, 2], Iter.drop(1), toArray), [2])
98+
deepStrictEqual(pipe([1, 2], Iter.drop(2), toArray), [])
99+
// out of bound
100+
deepStrictEqual(pipe(Iter.empty(), Iter.drop(1), toArray), [])
101+
deepStrictEqual(pipe(Iter.empty(), Iter.drop(-1), toArray), [])
102+
deepStrictEqual(pipe([1, 2], Iter.drop(3), toArray), [])
103+
deepStrictEqual(pipe([1, 2], Iter.drop(-1), toArray), [1, 2])
104+
105+
deepStrictEqual(pipe(new Set(), Iter.drop(0), toArray), [])
106+
deepStrictEqual(pipe(new Set([1, 2]), Iter.drop(0), toArray), [1, 2])
107+
deepStrictEqual(pipe(new Set([1, 2]), Iter.drop(1), toArray), [2])
108+
deepStrictEqual(pipe(new Set([1, 2]), Iter.drop(2), toArray), [])
109+
// out of bound
110+
deepStrictEqual(pipe(new Set(), Iter.drop(1), toArray), [])
111+
deepStrictEqual(pipe(new Set(), Iter.drop(-1), toArray), [])
112+
deepStrictEqual(pipe(new Set([1, 2]), Iter.drop(3), toArray), [])
113+
deepStrictEqual(pipe(new Set([1, 2]), Iter.drop(-1), toArray), [1, 2])
114+
})
115+
116+
describe("findFirst", () => {
117+
it("boolean-returning overloads", () => {
118+
deepStrictEqual(pipe([], Iter.findFirst((n) => n % 2 === 0)), O.none())
119+
deepStrictEqual(pipe([1, 2, 3], Iter.findFirst((n) => n % 2 === 0)), O.some(2))
120+
deepStrictEqual(pipe([1, 2, 3, 4], Iter.findFirst((n) => n % 2 === 0)), O.some(2))
121+
122+
deepStrictEqual(pipe(new Set<number>(), Iter.findFirst((n) => n % 2 === 0)), O.none())
123+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.findFirst((n) => n % 2 === 0)), O.some(2))
124+
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Iter.findFirst((n) => n % 2 === 0)), O.some(2))
125+
})
126+
127+
it("Option-returning overloads", () => {
128+
deepStrictEqual(pipe([], Iter.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())), O.none())
129+
deepStrictEqual(
130+
pipe([1, 2, 3], Iter.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
131+
O.some([2, 1])
132+
)
133+
deepStrictEqual(
134+
pipe([1, 2, 3, 4], Iter.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
135+
O.some([2, 1])
136+
)
137+
138+
deepStrictEqual(
139+
pipe(new Set<number>(), Iter.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
140+
O.none()
141+
)
142+
deepStrictEqual(
143+
pipe(new Set([1, 2, 3]), Iter.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
144+
O.some([2, 1])
145+
)
146+
deepStrictEqual(
147+
pipe(new Set([1, 2, 3, 4]), Iter.findFirst((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
148+
O.some([2, 1])
149+
)
150+
})
151+
})
152+
153+
describe("findLast", () => {
154+
it("boolean-returning overloads", () => {
155+
deepStrictEqual(pipe([], Iter.findLast((n) => n % 2 === 0)), O.none())
156+
deepStrictEqual(pipe([1, 2, 3], Iter.findLast((n) => n % 2 === 0)), O.some(2))
157+
deepStrictEqual(pipe([1, 2, 3, 4], Iter.findLast((n) => n % 2 === 0)), O.some(4))
158+
159+
deepStrictEqual(pipe(new Set<number>(), Iter.findLast((n) => n % 2 === 0)), O.none())
160+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.findLast((n) => n % 2 === 0)), O.some(2))
161+
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Iter.findLast((n) => n % 2 === 0)), O.some(4))
162+
})
163+
164+
it("Option-returning overloads", () => {
165+
deepStrictEqual(pipe([], Iter.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())), O.none())
166+
deepStrictEqual(
167+
pipe([1, 2, 3], Iter.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
168+
O.some([2, 1])
169+
)
170+
deepStrictEqual(
171+
pipe([1, 2, 3, 4], Iter.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
172+
O.some([4, 3])
173+
)
174+
175+
deepStrictEqual(
176+
pipe(new Set<number>(), Iter.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
177+
O.none()
178+
)
179+
deepStrictEqual(
180+
pipe(new Set([1, 2, 3]), Iter.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
181+
O.some([2, 1])
182+
)
183+
deepStrictEqual(
184+
pipe(new Set([1, 2, 3, 4]), Iter.findLast((n, i) => n % 2 === 0 ? O.some([n, i]) : O.none())),
185+
O.some([4, 3])
186+
)
187+
})
188+
})
189+
190+
it("zip", () => {
191+
deepStrictEqual(pipe(new Set([]), Iter.zip(new Set(["a", "b", "c", "d"])), toArray), [])
192+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.zip(new Set([])), toArray), [])
193+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.zip(new Set(["a", "b", "c", "d"])), toArray), [
194+
[1, "a"],
195+
[2, "b"],
196+
[3, "c"]
197+
])
198+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.zip(new Set(["a", "b", "c", "d"])), toArray), [
199+
[1, "a"],
200+
[2, "b"],
201+
[3, "c"]
202+
])
203+
})
204+
205+
it("zipWith", () => {
206+
deepStrictEqual(
207+
pipe(new Set([1, 2, 3]), Iter.zipWith(new Set([]), (n, s) => s + n), toArray),
208+
[]
209+
)
210+
deepStrictEqual(
211+
pipe(new Set([]), Iter.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n), toArray),
212+
[]
213+
)
214+
deepStrictEqual(
215+
pipe(new Set([]), Iter.zipWith(new Set([]), (n, s) => s + n), toArray),
216+
[]
217+
)
218+
deepStrictEqual(
219+
pipe(new Set([1, 2, 3]), Iter.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n), toArray),
220+
["a1", "b2", "c3"]
221+
)
222+
})
223+
224+
it("intersperse", () => {
225+
deepStrictEqual(pipe([], Iter.intersperse(0), toArray), [])
226+
deepStrictEqual(pipe([1], Iter.intersperse(0), toArray), [1])
227+
deepStrictEqual(pipe([1, 2, 3], Iter.intersperse(0), toArray), [1, 0, 2, 0, 3])
228+
deepStrictEqual(pipe([1, 2], Iter.intersperse(0), toArray), [1, 0, 2])
229+
deepStrictEqual(pipe([1, 2, 3, 4], Iter.intersperse(0), toArray), [1, 0, 2, 0, 3, 0, 4])
230+
231+
deepStrictEqual(pipe(new Set([]), Iter.intersperse(0), toArray), [])
232+
deepStrictEqual(pipe(new Set([1]), Iter.intersperse(0), toArray), [1])
233+
deepStrictEqual(pipe(new Set([1, 2, 3]), Iter.intersperse(0), toArray), [1, 0, 2, 0, 3])
234+
deepStrictEqual(pipe(new Set([1, 2]), Iter.intersperse(0), toArray), [1, 0, 2])
235+
deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Iter.intersperse(0), toArray), [1, 0, 2, 0, 3, 0, 4])
236+
})
237+
238+
it("containsWith", () => {
239+
const contains = Iter.containsWith(Number.Equivalence)
240+
deepStrictEqual(pipe([1, 2, 3], contains(2)), true)
241+
deepStrictEqual(pipe([1, 2, 3], contains(0)), false)
242+
243+
deepStrictEqual(pipe(new Set([1, 2, 3]), contains(2)), true)
244+
deepStrictEqual(pipe(new Set([1, 2, 3]), contains(0)), false)
245+
})
246+
247+
it("contains", () => {
248+
const contains = Iter.contains
249+
deepStrictEqual(pipe([1, 2, 3], contains(2)), true)
250+
deepStrictEqual(pipe([1, 2, 3], contains(0)), false)
251+
252+
deepStrictEqual(pipe(new Set([1, 2, 3]), contains(2)), true)
253+
deepStrictEqual(pipe(new Set([1, 2, 3]), contains(0)), false)
254+
})
255+
256+
it("dedupeAdjacentWith", () => {
257+
const dedupeAdjacent = Iter.dedupeAdjacentWith(Number.Equivalence)
258+
expect(toArray(dedupeAdjacent([]))).toEqual([])
259+
expect(toArray(dedupeAdjacent([1, 2, 3]))).toEqual([1, 2, 3])
260+
expect(toArray(dedupeAdjacent([1, 2, 2, 3, 3]))).toEqual([1, 2, 3])
261+
})
262+
})
263+
264+
it("flatMapNullable", () => {
265+
const f = Iter.flatMapNullable((n: number) => (n > 0 ? n : null))
266+
deepStrictEqual(pipe([], f, toArray), [])
267+
deepStrictEqual(pipe([1], f, toArray), [1])
268+
deepStrictEqual(pipe([-1], f, toArray), [])
269+
})
270+
271+
it("unfold", () => {
272+
const as = Iter.unfold(5, (n) => (n > 0 ? O.some([n, n - 1]) : O.none()))
273+
deepStrictEqual(toArray(as), [5, 4, 3, 2, 1])
274+
})
275+
276+
it("map", () => {
277+
deepStrictEqual(
278+
pipe([1, 2, 3], Iter.map((n) => n * 2), toArray),
279+
[2, 4, 6]
280+
)
281+
deepStrictEqual(
282+
pipe(["a", "b"], Iter.map((s, i) => s + i), toArray),
283+
["a0", "b1"]
284+
)
285+
})
286+
287+
it("flatMap", () => {
288+
deepStrictEqual(
289+
pipe([1, 2, 3], Iter.flatMap((n) => [n, n + 1]), toArray),
290+
[1, 2, 2, 3, 3, 4]
291+
)
292+
const f = (n: number, i: number) => [n + i]
293+
deepStrictEqual(pipe([], Iter.flatMap(f), toArray), [])
294+
deepStrictEqual(pipe([1, 2, 3], Iter.flatMap(f), toArray), [1, 3, 5])
295+
})
296+
297+
it("getSomes", () => {
298+
assert.deepStrictEqual(toArray(Iter.getSomes([])), [])
299+
assert.deepStrictEqual(toArray(Iter.getSomes([O.some(1), O.some(2), O.some(3)])), [
300+
1,
301+
2,
302+
3
303+
])
304+
assert.deepStrictEqual(toArray(Iter.getSomes([O.some(1), O.none(), O.some(3)])), [
305+
1,
306+
3
307+
])
308+
})
309+
310+
it("filter", () => {
311+
deepStrictEqual(toArray(Iter.filter([1, 2, 3], (n) => n % 2 === 1)), [1, 3])
312+
deepStrictEqual(toArray(Iter.filter([O.some(3), O.some(2), O.some(1)], O.isSome)), [
313+
O.some(3),
314+
O.some(2),
315+
O.some(1)
316+
])
317+
deepStrictEqual(toArray(Iter.filter([O.some(3), O.none(), O.some(1)], O.isSome)), [O.some(3), O.some(1)])
318+
deepStrictEqual(toArray(Iter.filter(["a", "b", "c"], (_, i) => i % 2 === 0)), ["a", "c"])
319+
})
320+
321+
it("filterMap", () => {
322+
const f = (n: number) => (n % 2 === 0 ? O.none() : O.some(n))
323+
deepStrictEqual(pipe([1, 2, 3], Iter.filterMap(f), toArray), [1, 3])
324+
deepStrictEqual(pipe([], Iter.filterMap(f), toArray), [])
325+
const g = (n: number, i: number) => ((i + n) % 2 === 0 ? O.none() : O.some(n))
326+
deepStrictEqual(pipe([1, 2, 4], Iter.filterMap(g), toArray), [1, 2])
327+
deepStrictEqual(pipe([], Iter.filterMap(g), toArray), [])
328+
})
329+
330+
it("isEmpty", () => {
331+
deepStrictEqual(Iter.isEmpty([1, 2, 3]), false)
332+
deepStrictEqual(Iter.isEmpty([]), true)
333+
})
334+
335+
it("head", () => {
336+
const as: ReadonlyArray<number> = [1, 2, 3]
337+
deepStrictEqual(Iter.head(as), O.some(1))
338+
deepStrictEqual(Iter.head([]), O.none())
339+
})
340+
341+
it("chunksOf", () => {
342+
deepStrictEqual(toArray(Iter.chunksOf(2)([1, 2, 3, 4, 5])), [
343+
[1, 2],
344+
[3, 4],
345+
[5]
346+
])
347+
deepStrictEqual(toArray(Iter.chunksOf(2)([1, 2, 3, 4, 5, 6])), [
348+
[1, 2],
349+
[3, 4],
350+
[5, 6]
351+
])
352+
deepStrictEqual(toArray(Iter.chunksOf(1)([1, 2, 3, 4, 5])), [[1], [2], [3], [4], [5]])
353+
deepStrictEqual(toArray(Iter.chunksOf(5)([1, 2, 3, 4, 5])), [[1, 2, 3, 4, 5]])
354+
// out of bounds
355+
deepStrictEqual(toArray(Iter.chunksOf(0)([1, 2, 3, 4, 5])), [[1], [2], [3], [4], [5]])
356+
deepStrictEqual(toArray(Iter.chunksOf(-1)([1, 2, 3, 4, 5])), [[1], [2], [3], [4], [5]])
357+
358+
const assertSingleChunk = (
359+
input: Iterable<number>,
360+
n: number
361+
) => {
362+
const chunks = toArray(Iter.chunksOf(n)(input))
363+
strictEqual(chunks.length, 1)
364+
deepStrictEqual(chunks[0], input)
365+
}
366+
// n = length
367+
assertSingleChunk([1, 2], 2)
368+
// n out of bounds
369+
assertSingleChunk([1, 2], 3)
370+
})
371+
372+
it("flatten", () => {
373+
expect(toArray(Iter.flatten([[1], [2], [3]]))).toEqual([1, 2, 3])
374+
})
375+
376+
it("groupWith", () => {
377+
const groupWith = Iter.groupWith(Number.Equivalence)
378+
deepStrictEqual(toArray(groupWith([1, 2, 1, 1])), [[1], [2], [1, 1]])
379+
deepStrictEqual(toArray(groupWith([1, 2, 1, 1, 3])), [[1], [2], [1, 1], [3]])
380+
})
381+
382+
it("groupBy", () => {
383+
deepStrictEqual(Iter.groupBy((_) => "")([]), {})
384+
deepStrictEqual(Iter.groupBy((a) => `${a}`)([1]), { "1": [1] })
385+
deepStrictEqual(
386+
Iter.groupBy((s: string) => `${s.length}`)(["foo", "bar", "foobar"]),
387+
{
388+
"3": ["foo", "bar"],
389+
"6": ["foobar"]
390+
}
391+
)
392+
expect(Iter.groupBy(["a", "b"], (s) => s === "a" ? symA : s === "b" ? symB : symC)).toStrictEqual({
393+
[symA]: ["a"],
394+
[symB]: ["b"]
395+
})
396+
expect(Iter.groupBy(["a", "b", "c", "d"], (s) => s === "a" ? symA : s === "b" ? symB : symC)).toStrictEqual({
397+
[symA]: ["a"],
398+
[symB]: ["b"],
399+
[symC]: ["c", "d"]
400+
})
401+
})
402+
403+
it("makeBy", () => {
404+
deepStrictEqual(
405+
pipe(
406+
Iter.makeBy((n) => n * 2),
407+
Iter.take(5),
408+
toArray
409+
),
410+
[0, 2, 4, 6, 8]
411+
)
412+
deepStrictEqual(toArray(Iter.makeBy((n) => n * 2, { length: 5 })), [0, 2, 4, 6, 8])
413+
deepStrictEqual(toArray(Iter.makeBy((n) => n * 2, { length: 2.2 })), [0, 2])
414+
})
415+
416+
it("replicate", () => {
417+
deepStrictEqual(toArray(Iter.replicate("a", 0)), ["a"])
418+
deepStrictEqual(toArray(Iter.replicate("a", -1)), ["a"])
419+
deepStrictEqual(toArray(Iter.replicate("a", 3)), ["a", "a", "a"])
420+
deepStrictEqual(toArray(Iter.replicate("a", 2.2)), ["a", "a"])
421+
})
422+
423+
it("range", () => {
424+
expect(toArray(Iter.range(0, 0))).toEqual([0])
425+
expect(toArray(Iter.range(0, 1))).toEqual([0, 1])
426+
expect(toArray(Iter.range(1, 5))).toEqual([1, 2, 3, 4, 5])
427+
expect(toArray(Iter.range(10, 15))).toEqual([10, 11, 12, 13, 14, 15])
428+
expect(toArray(Iter.range(-1, 0))).toEqual([-1, 0])
429+
expect(toArray(Iter.range(-5, -1))).toEqual([-5, -4, -3, -2, -1])
430+
// out of bound
431+
expect(Array.from(Iter.range(2, 1))).toEqual([2])
432+
expect(Array.from(Iter.range(-1, -2))).toEqual([-1])
433+
})
434+
435+
it("empty", () => {
436+
deepStrictEqual(toArray(Iter.empty()).length, 0)
437+
})
438+
439+
it("some", () => {
440+
const isPositive: Predicate<number> = (n) => n > 0
441+
expect(Iter.some([-1, -2, 3], isPositive)).toEqual(true)
442+
expect(Iter.some([-1, -2, -3], isPositive)).toEqual(false)
443+
})
444+
445+
it("size", () => {
446+
deepStrictEqual(Iter.size(Iter.empty()), 0)
447+
deepStrictEqual(Iter.size([]), 0)
448+
deepStrictEqual(Iter.size(["a"]), 1)
449+
})
450+
451+
it("forEach", () => {
452+
const log: Array<string> = []
453+
Iter.forEach(["a", "b", "c"], (a, i) => log.push(`${a}-${i}`))
454+
expect(log).toEqual(["a-0", "b-1", "c-2"])
455+
})
456+
})

0 commit comments

Comments
 (0)
Please sign in to comment.