1import { assert, describe, it } from "@effect/vitest"
2import { assertNone, assertSome, deepStrictEqual, strictEqual, throws } from "@effect/vitest/utils"
3import {
4 Array as Arr,
5 Equal,
6 Equivalence,
7 Hash,
8 Number as Num,
9 Option,
10 Order,
11 type Predicate,
12 Result,
13 String as Str
14} from "effect"
15import { identity, pipe } from "effect/Function"
16import * as fc from "fast-check"
17
18const symA = Symbol.for("a")
19const symB = Symbol.for("b")
20const symC = Symbol.for("c")
21
22const double = (n: number) => n * 2
23
24class HashCollision implements Equal.Equal {
25 constructor(readonly value: number) {}
26 [Hash.symbol](): number {
27 return 0
28 }
29 [Equal.symbol](that: Equal.Equal): boolean {
30 return that instanceof HashCollision && this.value === that.value
31 }
32}
33
34describe("Array", () => {
35 it("isCanonicalArrayIndex", () => {
36 assert.isTrue(Arr.isCanonicalArrayIndex("0"))
37 assert.isTrue(Arr.isCanonicalArrayIndex("4294967294"))
38 assert.isFalse(Arr.isCanonicalArrayIndex("01"))
39 assert.isFalse(Arr.isCanonicalArrayIndex("4294967295"))
40 })
41
42 it("of", () => {
43 deepStrictEqual(Arr.of(1), [1])
44 })
45
46 it("fromIterable/Array should return the same reference if the iterable is an Array", () => {
47 const i = [1, 2, 3]
48 strictEqual(Arr.fromIterable(i), i)
49 })
50
51 it("fromIterable/Iterable", () => {
52 deepStrictEqual(Arr.fromIterable(new Set([1, 2, 3])), [1, 2, 3])
53 })
54
55 it("ensure", () => {
56 deepStrictEqual(Arr.ensure(1), [1])
57 deepStrictEqual(Arr.ensure(null), [null])
58 deepStrictEqual(Arr.ensure([1]), [1])
59 deepStrictEqual(Arr.ensure([1, 2]), [1, 2])
60 deepStrictEqual(Arr.ensure(new Set([1, 2])), [new Set([1, 2])])
61 })
62
63 describe("iterable inputs", () => {
64 it("prepend", () => {
65 deepStrictEqual(pipe([1, 2, 3], Arr.prepend(0)), [0, 1, 2, 3])
66 deepStrictEqual(pipe([[2]], Arr.prepend([1])), [[1], [2]])
67
68 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.prepend(0)), [0, 1, 2, 3])
69 deepStrictEqual(pipe(new Set([[2]]), Arr.prepend([1])), [[1], [2]])
70 })
71
72 it("prependAll", () => {
73 deepStrictEqual(pipe([3, 4], Arr.prependAll([1, 2])), [1, 2, 3, 4])
74
75 deepStrictEqual(pipe([3, 4], Arr.prependAll(new Set([1, 2]))), [1, 2, 3, 4])
76 deepStrictEqual(pipe(new Set([3, 4]), Arr.prependAll([1, 2])), [1, 2, 3, 4])
77 })
78
79 it("append", () => {
80 deepStrictEqual(pipe([1, 2, 3], Arr.append(4)), [1, 2, 3, 4])
81 deepStrictEqual(pipe([[1]], Arr.append([2])), [[1], [2]])
82
83 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.append(4)), [1, 2, 3, 4])
84 deepStrictEqual(pipe(new Set([[1]]), Arr.append([2])), [[1], [2]])
85 })
86
87 it("appendAll", () => {
88 deepStrictEqual(pipe([1, 2], Arr.appendAll([3, 4])), [1, 2, 3, 4])
89
90 deepStrictEqual(pipe([1, 2], Arr.appendAll(new Set([3, 4]))), [1, 2, 3, 4])
91 deepStrictEqual(pipe(new Set([1, 2]), Arr.appendAll([3, 4])), [1, 2, 3, 4])
92 })
93
94 it("scan", () => {
95 const f = (b: number, a: number) => b - a
96 deepStrictEqual(pipe([1, 2, 3], Arr.scan(10, f)), [10, 9, 7, 4])
97 deepStrictEqual(pipe([0], Arr.scan(10, f)), [10, 10])
98 deepStrictEqual(pipe([], Arr.scan(10, f)), [10])
99
100 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.scan(10, f)), [10, 9, 7, 4])
101 deepStrictEqual(pipe(new Set([0]), Arr.scan(10, f)), [10, 10])
102 deepStrictEqual(pipe(new Set([]), Arr.scan(10, f)), [10])
103 })
104
105 it("scanRight", () => {
106 const f = (b: number, a: number) => a - b
107 deepStrictEqual(pipe([1, 2, 3], Arr.scanRight(10, f)), [-8, 9, -7, 10])
108 deepStrictEqual(pipe([0], Arr.scanRight(10, f)), [-10, 10])
109 deepStrictEqual(pipe([], Arr.scanRight(10, f)), [10])
110
111 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.scanRight(10, f)), [-8, 9, -7, 10])
112 deepStrictEqual(pipe(new Set([0]), Arr.scanRight(10, f)), [-10, 10])
113 deepStrictEqual(pipe(new Set([]), Arr.scanRight(10, f)), [10])
114 })
115
116 it("tail", () => {
117 assertSome(Arr.tail([1, 2, 3]), [2, 3])
118 assertNone(Arr.tail([]))
119
120 assertSome(Arr.tail(new Set([1, 2, 3])), [2, 3])
121 assertNone(Arr.tail(new Set([])))
122 })
123
124 it("init", () => {
125 assertSome(Arr.init([1, 2, 3]), [1, 2])
126 assertNone(Arr.init([]))
127
128 assertSome(Arr.init(new Set([1, 2, 3])), [1, 2])
129 assertNone(Arr.init(new Set([])))
130 })
131
132 it("take", () => {
133 deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(2)), [1, 2])
134 deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(0)), [])
135 deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(Number.NaN)), [])
136 // out of bounds
137 deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(-10)), [])
138 deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(10)), [1, 2, 3, 4])
139
140 deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(2)), [1, 2])
141 deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(0)), [])
142 // out of bounds
143 deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(-10)), [])
144 deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(10)), [1, 2, 3, 4])
145 })
146
147 it("takeRight", () => {
148 deepStrictEqual(pipe(Arr.empty(), Arr.takeRight(0)), [])
149 deepStrictEqual(pipe([1, 2], Arr.takeRight(0)), [])
150 deepStrictEqual(pipe([1, 2], Arr.takeRight(Number.NaN)), [])
151 deepStrictEqual(pipe([1, 2], Arr.takeRight(1)), [2])
152 deepStrictEqual(pipe([1, 2], Arr.takeRight(2)), [1, 2])
153 // out of bound
154 deepStrictEqual(pipe(Arr.empty(), Arr.takeRight(1)), [])
155 deepStrictEqual(pipe(Arr.empty(), Arr.takeRight(-1)), [])
156 deepStrictEqual(pipe([1, 2], Arr.takeRight(3)), [1, 2])
157 deepStrictEqual(pipe([1, 2], Arr.takeRight(-1)), [])
158
159 deepStrictEqual(pipe(new Set(), Arr.takeRight(0)), [])
160 deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(0)), [])
161 deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(1)), [2])
162 deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(2)), [1, 2])
163 // out of bound
164 deepStrictEqual(pipe(new Set(), Arr.takeRight(1)), [])
165 deepStrictEqual(pipe(new Set(), Arr.takeRight(-1)), [])
166 deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(3)), [1, 2])
167 deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(-1)), [])
168 })
169
170 it("takeWhile", () => {
171 const f = (n: number) => n % 2 === 0
172 deepStrictEqual(pipe([2, 4, 3, 6], Arr.takeWhile(f)), [2, 4])
173 deepStrictEqual(pipe(Arr.empty(), Arr.takeWhile(f)), [])
174 deepStrictEqual(pipe([1, 2, 4], Arr.takeWhile(f)), [])
175 deepStrictEqual(pipe([2, 4], Arr.takeWhile(f)), [2, 4])
176
177 deepStrictEqual(pipe(new Set([2, 4, 3, 6]), Arr.takeWhile(f)), [2, 4])
178 deepStrictEqual(pipe(new Set<number>(), Arr.takeWhile(f)), [])
179 deepStrictEqual(pipe(new Set([1, 2, 4]), Arr.takeWhile(f)), [])
180 deepStrictEqual(pipe(new Set([2, 4]), Arr.takeWhile(f)), [2, 4])
181 })
182
183 it("takeWhileFilter", () => {
184 const f: (n: number, i: number) => Result.Result<number, string> = (n, i) =>
185 n + i < 5 ? Result.succeed(n * 10) : Result.fail(`stop: ${n}`)
186
187 deepStrictEqual(Arr.takeWhileFilter([], f), [])
188 deepStrictEqual(Arr.takeWhileFilter([1, 2, 3], f), [10, 20])
189 deepStrictEqual(pipe([1, 2, 3], Arr.takeWhileFilter(f)), [10, 20])
190
191 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.takeWhileFilter(f)), [10, 20])
192 deepStrictEqual(pipe([][Symbol.iterator](), Arr.takeWhileFilter(f)), [])
193 })
194
195 it("span", () => {
196 const f = Arr.span<number>((n) => n % 2 === 1)
197 const assertSpan = (
198 input: Iterable<number>,
199 expectedInit: ReadonlyArray<number>,
200 expectedRest: ReadonlyArray<number>
201 ) => {
202 const [init, rest] = f(input)
203 deepStrictEqual(init, expectedInit)
204 deepStrictEqual(rest, expectedRest)
205 }
206 assertSpan([1, 3, 2, 4, 5], [1, 3], [2, 4, 5])
207 assertSpan(Arr.empty(), Arr.empty(), Arr.empty())
208 assertSpan([1, 3], [1, 3], Arr.empty())
209 assertSpan([2, 4], Arr.empty(), [2, 4])
210
211 assertSpan(new Set([1, 3, 2, 4, 5]), [1, 3], [2, 4, 5])
212 assertSpan(new Set(), Arr.empty(), Arr.empty())
213 assertSpan(new Set([1, 3]), [1, 3], Arr.empty())
214 assertSpan(new Set([2, 4]), Arr.empty(), [2, 4])
215
216 // iterator input
217 assertSpan([1, 3, 2, 4, 5][Symbol.iterator](), [1, 3], [2, 4, 5])
218 assertSpan([][Symbol.iterator](), Arr.empty(), Arr.empty())
219 })
220
221 it("splitWhere", () => {
222 const f = Arr.splitWhere<number>((n) => n % 2 !== 1)
223 const assertSplitWhere = (
224 input: Iterable<number>,
225 expectedInit: ReadonlyArray<number>,
226 expectedRest: ReadonlyArray<number>
227 ) => {
228 const [init, rest] = f(input)
229 deepStrictEqual(init, expectedInit)
230 deepStrictEqual(rest, expectedRest)
231 }
232 assertSplitWhere([1, 3, 2, 4, 5], [1, 3], [2, 4, 5])
233 assertSplitWhere(Arr.empty(), Arr.empty(), Arr.empty())
234 assertSplitWhere([1, 3], [1, 3], Arr.empty())
235 assertSplitWhere([2, 4], Arr.empty(), [2, 4])
236
237 assertSplitWhere(new Set([1, 3, 2, 4, 5]), [1, 3], [2, 4, 5])
238 assertSplitWhere(new Set(), Arr.empty(), Arr.empty())
239 assertSplitWhere(new Set([1, 3]), [1, 3], Arr.empty())
240 assertSplitWhere(new Set([2, 4]), Arr.empty(), [2, 4])
241 })
242
243 it("split", () => {
244 deepStrictEqual(pipe(Arr.empty(), Arr.split(2)), Arr.empty())
245 deepStrictEqual(pipe(Arr.make(1), Arr.split(2)), Arr.make(Arr.make(1)))
246 deepStrictEqual(pipe(Arr.make(1, 2, 3), Arr.split(Number.NaN)), Arr.make(Arr.make(1, 2, 3)))
247 deepStrictEqual(pipe(Arr.make(1, 2), Arr.split(2)), Arr.make(Arr.make(1), Arr.make(2)))
248 deepStrictEqual(pipe(Arr.make(1, 2, 3, 4, 5), Arr.split(2)), Arr.make(Arr.make(1, 2, 3), Arr.make(4, 5)))
249 deepStrictEqual(
250 pipe(Arr.make(1, 2, 3, 4, 5), Arr.split(3)),
251 Arr.make(Arr.make(1, 2), Arr.make(3, 4), Arr.make(5))
252 )
253 })
254
255 it("drop", () => {
256 deepStrictEqual(pipe(Arr.empty(), Arr.drop(0)), [])
257 deepStrictEqual(pipe([1, 2], Arr.drop(0)), [1, 2])
258 deepStrictEqual(pipe([1, 2], Arr.drop(Number.NaN)), [1, 2])
259 deepStrictEqual(pipe([1, 2], Arr.drop(1)), [2])
260 deepStrictEqual(pipe([1, 2], Arr.drop(2)), [])
261 // out of bound
262 deepStrictEqual(pipe(Arr.empty(), Arr.drop(1)), [])
263 deepStrictEqual(pipe(Arr.empty(), Arr.drop(-1)), [])
264 deepStrictEqual(pipe([1, 2], Arr.drop(3)), [])
265 deepStrictEqual(pipe([1, 2], Arr.drop(-1)), [1, 2])
266
267 deepStrictEqual(pipe(new Set(), Arr.drop(0)), [])
268 deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(0)), [1, 2])
269 deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(1)), [2])
270 deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(2)), [])
271 // out of bound
272 deepStrictEqual(pipe(new Set(), Arr.drop(1)), [])
273 deepStrictEqual(pipe(new Set(), Arr.drop(-1)), [])
274 deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(3)), [])
275 deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(-1)), [1, 2])
276 })
277
278 it("dropRight", () => {
279 deepStrictEqual(pipe([], Arr.dropRight(0)), [])
280 deepStrictEqual(pipe([1, 2], Arr.dropRight(0)), [1, 2])
281 deepStrictEqual(pipe([1, 2], Arr.dropRight(Number.NaN)), [1, 2])
282 deepStrictEqual(pipe([1, 2], Arr.dropRight(1)), [1])
283 deepStrictEqual(pipe([1, 2], Arr.dropRight(2)), [])
284 // out of bound
285 deepStrictEqual(pipe([], Arr.dropRight(1)), [])
286 deepStrictEqual(pipe([1, 2], Arr.dropRight(3)), [])
287 deepStrictEqual(pipe([], Arr.dropRight(-1)), [])
288 deepStrictEqual(pipe([1, 2], Arr.dropRight(-1)), [1, 2])
289
290 deepStrictEqual(pipe(new Set(), Arr.dropRight(0)), [])
291 deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(0)), [1, 2])
292 deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(1)), [1])
293 deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(2)), [])
294 // out of bound
295 deepStrictEqual(pipe(new Set(), Arr.dropRight(1)), [])
296 deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(3)), [])
297 deepStrictEqual(pipe(new Set(), Arr.dropRight(-1)), [])
298 deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(-1)), [1, 2])
299 })
300
301 it("dropWhile", () => {
302 const f = Arr.dropWhile<number>((n) => n > 0)
303
304 deepStrictEqual(f([]), [])
305 deepStrictEqual(f([1, 2]), Arr.empty())
306 deepStrictEqual(f([-1, -2]), [-1, -2])
307 deepStrictEqual(f([-1, 2]), [-1, 2])
308 deepStrictEqual(f([1, -2, 3]), [-2, 3])
309
310 deepStrictEqual(f(new Set<number>()), [])
311 deepStrictEqual(f(new Set([1, 2])), Arr.empty())
312 deepStrictEqual(f(new Set([-1, -2])), [-1, -2])
313 deepStrictEqual(f(new Set([-1, 2])), [-1, 2])
314 deepStrictEqual(f(new Set([1, -2, 3])), [-2, 3])
315
316 // iterator input
317 deepStrictEqual(f([1, -2, 3][Symbol.iterator]()), [-2, 3])
318 deepStrictEqual(f([][Symbol.iterator]()), [])
319 })
320
321 it("dropWhileFilter", () => {
322 const f: (n: number, i: number) => Result.Result<number, string> = (n, i) =>
323 n + i < 5 ? Result.succeed(n * 10) : Result.fail(`stop: ${n}`)
324
325 deepStrictEqual(Arr.dropWhileFilter([], f), [])
326 deepStrictEqual(Arr.dropWhileFilter([1, 2, 3], f), [3])
327 deepStrictEqual(pipe([1, 2, 3], Arr.dropWhileFilter(f)), [3])
328
329 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.dropWhileFilter(f)), [3])
330 deepStrictEqual(pipe([][Symbol.iterator](), Arr.dropWhileFilter(f)), [])
331 })
332
333 it("findFirstIndex", () => {
334 assertNone(pipe([], Arr.findFirstIndex((n) => n % 2 === 0)))
335 assertSome(pipe([1, 2, 3], Arr.findFirstIndex((n) => n % 2 === 0)), 1)
336 assertSome(pipe([1, 2, 3, 1], Arr.findFirstIndex((n) => n % 2 === 0)), 1)
337
338 assertNone(pipe(new Set<number>(), Arr.findFirstIndex((n) => n % 2 === 0)))
339 assertSome(pipe(new Set([1, 2, 3]), Arr.findFirstIndex((n) => n % 2 === 0)), 1)
340 assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findFirstIndex((n) => n % 2 === 0)), 1)
341 })
342
343 it("findLastIndex", () => {
344 assertNone(pipe([], Arr.findLastIndex((n) => n % 2 === 0)))
345 assertSome(pipe([1, 2, 3], Arr.findLastIndex((n) => n % 2 === 0)), 1)
346 assertSome(pipe([1, 2, 3, 4], Arr.findLastIndex((n) => n % 2 === 0)), 3)
347
348 assertNone(pipe(new Set<number>(), Arr.findLastIndex((n) => n % 2 === 0)))
349 assertSome(pipe(new Set([1, 2, 3]), Arr.findLastIndex((n) => n % 2 === 0)), 1)
350 assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findLastIndex((n) => n % 2 === 0)), 3)
351 })
352
353 describe("findFirst", () => {
354 it("boolean-returning overloads", () => {
355 assertNone(pipe([], Arr.findFirst((n) => n % 2 === 0)))
356 assertSome(pipe([1, 2, 3], Arr.findFirst((n) => n % 2 === 0)), 2)
357 assertSome(pipe([1, 2, 3, 4], Arr.findFirst((n) => n % 2 === 0)), 2)
358
359 assertNone(pipe(new Set<number>(), Arr.findFirst((n) => n % 2 === 0)))
360 assertSome(pipe(new Set([1, 2, 3]), Arr.findFirst((n) => n % 2 === 0)), 2)
361 assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findFirst((n) => n % 2 === 0)), 2)
362 })
363
364 it("Option-returning overloads", () => {
365 assertNone(
366 pipe([], Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none()))
367 )
368 assertSome(
369 pipe([1, 2, 3], Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
370 [2, 1]
371 )
372 assertSome(
373 pipe([1, 2, 3, 4], Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
374 [2, 1]
375 )
376
377 assertNone(
378 pipe(new Set<number>(), Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none()))
379 )
380 assertSome(
381 pipe(new Set([1, 2, 3]), Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
382 [2, 1]
383 )
384 assertSome(
385 pipe(new Set([1, 2, 3, 4]), Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
386 [2, 1]
387 )
388 })
389 })
390
391 describe("findFirstWithIndex", () => {
392 it("boolean-returning overloads", () => {
393 assertNone(pipe([], Arr.findFirstWithIndex((n) => n % 2 === 0)))
394 assertSome(pipe([1, 2, 3], Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1])
395 assertSome(pipe([1, 2, 3, 4], Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1])
396
397 assertNone(pipe(new Set<number>(), Arr.findFirstWithIndex((n) => n % 2 === 0)))
398 assertSome(pipe(new Set([1, 2, 3]), Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1])
399 assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1])
400 })
401
402 it("Option-returning overloads", () => {
403 assertNone(
404 pipe([], Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none()))
405 )
406 assertSome(
407 pipe([1, 2, 3], Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())),
408 [3, 1]
409 )
410 assertSome(
411 pipe([1, 2, 3, 4], Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())),
412 [3, 1]
413 )
414
415 assertNone(
416 pipe(new Set<number>(), Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none()))
417 )
418 assertSome(
419 pipe(new Set([1, 2, 3]), Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())),
420 [3, 1]
421 )
422 assertSome(
423 pipe(new Set([1, 2, 3, 4]), Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())),
424 [3, 1]
425 )
426 })
427 })
428
429 describe("findLast", () => {
430 it("boolean-returning overloads", () => {
431 assertNone(pipe([], Arr.findLast((n) => n % 2 === 0)))
432 assertSome(pipe([1, 2, 3], Arr.findLast((n) => n % 2 === 0)), 2)
433 assertSome(pipe([1, 2, 3, 4], Arr.findLast((n) => n % 2 === 0)), 4)
434
435 assertNone(pipe(new Set<number>(), Arr.findLast((n) => n % 2 === 0)))
436 assertSome(pipe(new Set([1, 2, 3]), Arr.findLast((n) => n % 2 === 0)), 2)
437 assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findLast((n) => n % 2 === 0)), 4)
438 })
439
440 it("Option-returning overloads", () => {
441 assertNone(
442 pipe([], Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none()))
443 )
444 assertSome(
445 pipe([1, 2, 3], Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
446 [2, 1]
447 )
448 assertSome(
449 pipe([1, 2, 3, 4], Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
450 [4, 3]
451 )
452
453 assertNone(
454 pipe(new Set<number>(), Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none()))
455 )
456 assertSome(
457 pipe(new Set([1, 2, 3]), Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
458 [2, 1]
459 )
460 assertSome(
461 pipe(new Set([1, 2, 3, 4]), Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())),
462 [4, 3]
463 )
464 })
465 })
466
467 it("insertAt", () => {
468 assertNone(Arr.insertAt(1, 1)([]))
469 assertSome(Arr.insertAt(0, 1)([]), [1])
470 assertSome(Arr.insertAt(2, 5)([1, 2, 3, 4]), [1, 2, 5, 3, 4])
471 // out of bound
472 assertNone(Arr.insertAt(-1, 5)([1, 2, 3, 4]))
473 assertNone(Arr.insertAt(10, 5)([1, 2, 3, 4]))
474
475 assertNone(Arr.insertAt(1, 1)(new Set([])))
476 assertSome(Arr.insertAt(0, 1)(new Set([])), [1])
477 assertSome(Arr.insertAt(2, 5)(new Set([1, 2, 3, 4])), [1, 2, 5, 3, 4])
478 // out of bound
479 assertNone(Arr.insertAt(-1, 5)(new Set([1, 2, 3, 4])))
480 assertNone(Arr.insertAt(10, 5)(new Set([1, 2, 3, 4])))
481 })
482
483 it("replace", () => {
484 assertSome(pipe([1, 2, 3], Arr.replace(1, "a")), [1, "a", 3])
485 // out of bound
486 assertNone(pipe([], Arr.replace(1, "a")))
487 assertNone(pipe([1, 2, 3], Arr.replace(-1, "a")))
488 assertNone(pipe([1, 2, 3], Arr.replace(10, "a")))
489
490 assertSome(pipe(new Set([1, 2, 3]), Arr.replace(1, "a")), [1, "a", 3])
491 // out of bound
492 assertNone(pipe(new Set([]), Arr.replace(1, "a")))
493 assertNone(pipe(new Set([1, 2, 3]), Arr.replace(-1, "a")))
494 assertNone(pipe(new Set([1, 2, 3]), Arr.replace(10, "a")))
495 })
496
497 it("modify", () => {
498 assertSome(pipe([1, 2, 3], Arr.modify(1, double)), [1, 4, 3])
499 // out of bound
500 assertNone(pipe([], Arr.modify(1, double)))
501 assertNone(pipe([1, 2, 3], Arr.modify(10, double)))
502
503 assertSome(pipe(new Set([1, 2, 3]), Arr.modify(1, double)), [1, 4, 3])
504 // out of bound
505 assertNone(pipe(new Set([]), Arr.modify(1, double)))
506 assertNone(pipe(new Set([1, 2, 3]), Arr.modify(10, double)))
507 })
508
509 it("remove", () => {
510 deepStrictEqual(pipe([1, 2, 3], Arr.remove(0)), [2, 3])
511 // out of bound
512 deepStrictEqual(pipe([], Arr.remove(0)), [])
513 deepStrictEqual(pipe([1, 2, 3], Arr.remove(-1)), [1, 2, 3])
514 deepStrictEqual(pipe([1, 2, 3], Arr.remove(10)), [1, 2, 3])
515
516 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.remove(0)), [2, 3])
517 // out of bound
518 deepStrictEqual(pipe(new Set([]), Arr.remove(0)), [])
519 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.remove(-1)), [1, 2, 3])
520 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.remove(10)), [1, 2, 3])
521 })
522
523 it("reverse", () => {
524 deepStrictEqual(Arr.reverse([]), [])
525 deepStrictEqual(Arr.reverse([1]), [1])
526 deepStrictEqual(Arr.reverse([1, 2, 3]), [3, 2, 1])
527
528 deepStrictEqual(Arr.reverse(new Set([])), [])
529 deepStrictEqual(Arr.reverse(new Set([1])), [1])
530 deepStrictEqual(Arr.reverse(new Set([1, 2, 3])), [3, 2, 1])
531 })
532
533 it("sort", () => {
534 deepStrictEqual(Arr.sort(Num.Order)([]), [])
535 deepStrictEqual(Arr.sort(Num.Order)([1, 3, 2]), [1, 2, 3])
536
537 deepStrictEqual(Arr.sort(Num.Order)(new Set<number>()), [])
538 deepStrictEqual(Arr.sort(Num.Order)(new Set([1, 3, 2])), [1, 2, 3])
539 })
540
541 it("zip", () => {
542 deepStrictEqual(pipe(new Set([]), Arr.zip(new Set(["a", "b", "c", "d"]))), [])
543 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.zip(new Set([]))), [])
544 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.zip(new Set(["a", "b", "c", "d"]))), [
545 [1, "a"],
546 [2, "b"],
547 [3, "c"]
548 ])
549 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.zip(new Set(["a", "b", "c", "d"]))), [
550 [1, "a"],
551 [2, "b"],
552 [3, "c"]
553 ])
554 })
555
556 it("zipWith", () => {
557 deepStrictEqual(
558 pipe(new Set([1, 2, 3]), Arr.zipWith(new Set([]), (n, s) => s + n)),
559 []
560 )
561 deepStrictEqual(
562 pipe(new Set([]), Arr.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n)),
563 []
564 )
565 deepStrictEqual(
566 pipe(new Set([]), Arr.zipWith(new Set([]), (n, s) => s + n)),
567 []
568 )
569 deepStrictEqual(
570 pipe(new Set([1, 2, 3]), Arr.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n)),
571 ["a1", "b2", "c3"]
572 )
573 })
574
575 it("unzip", () => {
576 deepStrictEqual(Arr.unzip(new Set([])), [[], []])
577 deepStrictEqual(
578 Arr.unzip(
579 new Set(
580 [
581 [1, "a"],
582 [2, "b"],
583 [3, "c"]
584 ] as const
585 )
586 ),
587 [
588 [1, 2, 3],
589 ["a", "b", "c"]
590 ]
591 )
592 })
593
594 it("intersperse", () => {
595 deepStrictEqual(pipe([], Arr.intersperse(0)), [])
596 deepStrictEqual(pipe([1], Arr.intersperse(0)), [1])
597 deepStrictEqual(pipe([1, 2, 3], Arr.intersperse(0)), [1, 0, 2, 0, 3])
598 deepStrictEqual(pipe([1, 2], Arr.intersperse(0)), [1, 0, 2])
599 deepStrictEqual(pipe([1, 2, 3, 4], Arr.intersperse(0)), [1, 0, 2, 0, 3, 0, 4])
600
601 deepStrictEqual(pipe(new Set([]), Arr.intersperse(0)), [])
602 deepStrictEqual(pipe(new Set([1]), Arr.intersperse(0)), [1])
603 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.intersperse(0)), [1, 0, 2, 0, 3])
604 deepStrictEqual(pipe(new Set([1, 2]), Arr.intersperse(0)), [1, 0, 2])
605 deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.intersperse(0)), [1, 0, 2, 0, 3, 0, 4])
606 })
607
608 it("rotate", () => {
609 deepStrictEqual(Arr.rotate(0)(Arr.empty()), Arr.empty())
610 deepStrictEqual(Arr.rotate(1)(Arr.empty()), Arr.empty())
611 deepStrictEqual(Arr.rotate(1)([1]), [1])
612 deepStrictEqual(Arr.rotate(2)([1]), [1])
613 deepStrictEqual(Arr.rotate(-1)([1]), [1])
614 deepStrictEqual(Arr.rotate(-2)([1]), [1])
615 deepStrictEqual(Arr.rotate(2)([1, 2]), [1, 2])
616 deepStrictEqual(Arr.rotate(0)([1, 2]), [1, 2])
617 deepStrictEqual(Arr.rotate(-2)([1, 2]), [1, 2])
618 deepStrictEqual(Arr.rotate(1)([1, 2]), [2, 1])
619 deepStrictEqual(Arr.rotate(1)(new Set([1, 2, 3, 4, 5])), [5, 1, 2, 3, 4])
620 deepStrictEqual(Arr.rotate(2)(new Set([1, 2, 3, 4, 5])), [4, 5, 1, 2, 3])
621 deepStrictEqual(Arr.rotate(-1)(new Set([1, 2, 3, 4, 5])), [2, 3, 4, 5, 1])
622 deepStrictEqual(Arr.rotate(-2)(new Set([1, 2, 3, 4, 5])), [3, 4, 5, 1, 2])
623 deepStrictEqual(
624 Arr.rotate(1)((function*() {
625 yield* [1, 2, 3]
626 })()),
627 [3, 1, 2]
628 )
629 // out of bounds
630 deepStrictEqual(Arr.rotate(7)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3])
631 deepStrictEqual(Arr.rotate(-7)([1, 2, 3, 4, 5]), [3, 4, 5, 1, 2])
632 deepStrictEqual(Arr.rotate(2.2)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3])
633 deepStrictEqual(Arr.rotate(-2.2)([1, 2, 3, 4, 5]), [3, 4, 5, 1, 2])
634 })
635
636 it("containsWith", () => {
637 const contains = Arr.containsWith(Equivalence.strictEqual<number>())
638 deepStrictEqual(pipe([1, 2, 3], contains(2)), true)
639 deepStrictEqual(pipe([1, 2, 3], contains(0)), false)
640
641 deepStrictEqual(pipe(new Set([1, 2, 3]), contains(2)), true)
642 deepStrictEqual(pipe(new Set([1, 2, 3]), contains(0)), false)
643 })
644
645 it("contains", () => {
646 const contains = Arr.contains
647 deepStrictEqual(pipe([1, 2, 3], contains(2)), true)
648 deepStrictEqual(pipe([1, 2, 3], contains(0)), false)
649
650 deepStrictEqual(pipe(new Set([1, 2, 3]), contains(2)), true)
651 deepStrictEqual(pipe(new Set([1, 2, 3]), contains(0)), false)
652 })
653
654 it("dedupeWith", () => {
655 const dedupe = Arr.dedupeWith(Equivalence.strictEqual<number>())
656 deepStrictEqual(dedupe([]), [])
657 deepStrictEqual(dedupe([-0, -0]), [-0])
658 deepStrictEqual(dedupe([0, -0]), [0])
659 deepStrictEqual(dedupe([1]), [1])
660 deepStrictEqual(dedupe([2, 1, 2]), [2, 1])
661 deepStrictEqual(dedupe([1, 2, 1]), [1, 2])
662 deepStrictEqual(dedupe([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
663 deepStrictEqual(dedupe([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]), [1, 2, 3, 4, 5])
664 deepStrictEqual(dedupe([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
665 })
666
667 it("dedupeAdjacentWith", () => {
668 const dedupeAdjacent = Arr.dedupeAdjacentWith(Equivalence.strictEqual<number>())
669 deepStrictEqual(dedupeAdjacent([]), [])
670 deepStrictEqual(dedupeAdjacent([1, 2, 3]), [1, 2, 3])
671 deepStrictEqual(dedupeAdjacent([1, 2, 2, 3, 3]), [1, 2, 3])
672 })
673
674 it("splitAt", () => {
675 const assertSplitAt = (
676 input: ReadonlyArray<number>,
677 index: number,
678 expectedInit: ReadonlyArray<number>,
679 expectedRest: ReadonlyArray<number>
680 ) => {
681 const [init, rest] = Arr.splitAt(index)(input)
682 deepStrictEqual(init, expectedInit)
683 deepStrictEqual(rest, expectedRest)
684 }
685 deepStrictEqual(Arr.splitAt(1)([1, 2]), [[1], [2]])
686 assertSplitAt([1, 2], 2, [1, 2], [])
687 deepStrictEqual(Arr.splitAt(2)([1, 2, 3, 4, 5]), [
688 [1, 2],
689 [3, 4, 5]
690 ])
691 deepStrictEqual(Arr.splitAt(2)(new Set([1, 2, 3, 4, 5])), [
692 [1, 2],
693 [3, 4, 5]
694 ])
695 assertSplitAt([], 0, [], [])
696 assertSplitAt([1, 2], 0, [], [1, 2])
697 assertSplitAt([1, 2], Number.NaN, [], [1, 2])
698
699 // out of bounds
700 assertSplitAt([], -1, [], [])
701 assertSplitAt([1, 2], -1, [], [1, 2])
702 assertSplitAt([1, 2], 3, [1, 2], [])
703 assertSplitAt([], 3, [], [])
704 })
705 })
706
707 it("splitAtNonEmpty", () => {
708 deepStrictEqual(pipe(Arr.make(1, 2, 3, 4), Arr.splitAtNonEmpty(2)), [[1, 2], [3, 4]])
709 deepStrictEqual(pipe(Arr.make(1, 2, 3, 4), Arr.splitAtNonEmpty(10)), [[1, 2, 3, 4], []])
710 deepStrictEqual(pipe(Arr.make(1, 2, 3, 4), Arr.splitAtNonEmpty(Number.NaN)), [[1], [2, 3, 4]])
711 })
712
713 describe("getUnsafe", () => {
714 it("should throw on index out of bound", () => {
715 throws(() => pipe([], Arr.getUnsafe(100)), new Error("Index out of bounds: 100"))
716 })
717 })
718
719 it("fromNullishOr", () => {
720 deepStrictEqual(Arr.fromNullishOr(undefined), [])
721 deepStrictEqual(Arr.fromNullishOr(null), [])
722 deepStrictEqual(Arr.fromNullishOr(1), [1])
723 })
724
725 it("liftNullishOr", () => {
726 const f = Arr.liftNullishOr((n: number) => (n > 0 ? n : null))
727 deepStrictEqual(f(1), [1])
728 deepStrictEqual(f(-1), [])
729 })
730
731 it("flatMapNullishOr", () => {
732 const f = Arr.flatMapNullishOr((n: number) => (n > 0 ? n : null))
733 deepStrictEqual(pipe([], f), [])
734 deepStrictEqual(pipe([1], f), [1])
735 deepStrictEqual(pipe([1, 2], f), [1, 2])
736 deepStrictEqual(pipe([-1], f), [])
737 deepStrictEqual(pipe([-1, 2], f), [2])
738 })
739
740 it("liftPredicate", () => {
741 const p = (n: number): boolean => n > 2
742 const f = Arr.liftPredicate(p)
743 deepStrictEqual(f(1), [])
744 deepStrictEqual(f(3), [3])
745 })
746
747 it("liftOption", () => {
748 const f = Arr.liftOption((n: number) => (n > 0 ? Option.some(n) : Option.none()))
749 deepStrictEqual(f(1), [1])
750 deepStrictEqual(f(-1), [])
751 })
752
753 it("unprepend", () => {
754 deepStrictEqual(Arr.unprepend([0]), [0, []])
755 deepStrictEqual(Arr.unprepend([1, 2, 3, 4]), [1, [2, 3, 4]])
756 })
757
758 it("unappend", () => {
759 deepStrictEqual(Arr.unappend([0]), [[], 0])
760 deepStrictEqual(Arr.unappend([1, 2, 3, 4]), [
761 Arr.make(1, 2, 3),
762 4
763 ])
764 deepStrictEqual(Arr.unappend([0]), [[], 0])
765 deepStrictEqual(Arr.unappend([1, 2, 3, 4]), [
766 Arr.make(1, 2, 3),
767 4
768 ])
769 })
770
771 it("modifyHeadNonEmpty", () => {
772 const f = (s: string) => s + "!"
773 deepStrictEqual(pipe(["a"], Arr.modifyHeadNonEmpty(f)), ["a!"])
774 deepStrictEqual(pipe(["a", "b"], Arr.modifyHeadNonEmpty(f)), ["a!", "b"])
775 deepStrictEqual(pipe(["a", "b", "c"], Arr.modifyHeadNonEmpty(f)), ["a!", "b", "c"])
776 })
777
778 it("modifyLastNonEmpty", () => {
779 const f = (s: string) => s + "!"
780 deepStrictEqual(pipe(["a"], Arr.modifyLastNonEmpty(f)), ["a!"])
781 deepStrictEqual(pipe(["a", "b"], Arr.modifyLastNonEmpty(f)), ["a", "b!"])
782 deepStrictEqual(pipe(["a", "b", "c"], Arr.modifyLastNonEmpty(f)), ["a", "b", "c!"])
783 })
784
785 it("setHeadNonEmpty", () => {
786 deepStrictEqual(pipe(Arr.make("a"), Arr.setHeadNonEmpty("d")), ["d"])
787 deepStrictEqual(pipe(Arr.make("a", "b"), Arr.setHeadNonEmpty("d")), ["d", "b"])
788 deepStrictEqual(pipe(Arr.make("a", "b", "c"), Arr.setHeadNonEmpty("d")), ["d", "b", "c"])
789 })
790
791 it("setLastNonEmpty", () => {
792 deepStrictEqual(pipe(Arr.make("a"), Arr.setLastNonEmpty("d")), ["d"])
793 deepStrictEqual(pipe(Arr.make("a", "b"), Arr.setLastNonEmpty("d")), ["a", "d"])
794 deepStrictEqual(pipe(Arr.make("a", "b", "c"), Arr.setLastNonEmpty("d")), ["a", "b", "d"])
795 })
796
797 it("liftResult", () => {
798 const f = Arr.liftResult((s: string) => s.length > 2 ? Result.succeed(s.length) : Result.fail("e"))
799 deepStrictEqual(f("a"), [])
800 deepStrictEqual(f("aaa"), [3])
801 })
802
803 it("headNonEmpty", () => {
804 deepStrictEqual(Arr.headNonEmpty(Arr.make(1, 2)), 1)
805 })
806
807 it("tailNonEmpty", () => {
808 deepStrictEqual(Arr.tailNonEmpty(Arr.make(1, 2)), [2])
809 })
810
811 it("lastNonEmpty", () => {
812 deepStrictEqual(Arr.lastNonEmpty(Arr.make(1, 2, 3)), 3)
813 deepStrictEqual(Arr.lastNonEmpty([1]), 1)
814 })
815
816 it("initNonEmpty", () => {
817 deepStrictEqual(
818 Arr.initNonEmpty(Arr.make(1, 2, 3)),
819 Arr.make(1, 2)
820 )
821 deepStrictEqual(Arr.initNonEmpty([1]), [])
822 })
823
824 it("get", () => {
825 assertSome(pipe([1, 2, 3], Arr.get(0)), 1)
826 assertNone(pipe([1, 2, 3], Arr.get(3)))
827 })
828
829 it("unfold", () => {
830 const as = Arr.unfold(5, (n) => (n > 0 ? Option.some([n, n - 1]) : Option.none()))
831 deepStrictEqual(as, [5, 4, 3, 2, 1])
832 })
833
834 it("map", () => {
835 deepStrictEqual(
836 pipe([1, 2, 3], Arr.map((n) => n * 2)),
837 [2, 4, 6]
838 )
839 deepStrictEqual(
840 pipe(["a", "b"], Arr.map((s, i) => s + i)),
841 ["a0", "b1"]
842 )
843 })
844
845 it("flatMap", () => {
846 deepStrictEqual(
847 pipe([1, 2, 3], Arr.flatMap((n) => [n, n + 1])),
848 [1, 2, 2, 3, 3, 4]
849 )
850 const f = Arr.flatMap((n: number, i) => [n + i])
851 deepStrictEqual(pipe([], f), [])
852 deepStrictEqual(pipe([1, 2, 3], f), [1, 3, 5])
853 })
854
855 it("extend", () => {
856 deepStrictEqual(pipe([1, 2, 3, 4], Arr.extend(Num.sumAll)), [10, 9, 7, 4])
857 deepStrictEqual(pipe([1, 2, 3, 4], Arr.extend(identity)), [
858 [1, 2, 3, 4],
859 [2, 3, 4],
860 [3, 4],
861 [4]
862 ])
863 })
864
865 it("compact", () => {
866 deepStrictEqual(Arr.getSomes([]), [])
867 deepStrictEqual(Arr.getSomes([Option.some(1), Option.some(2), Option.some(3)]), [
868 1,
869 2,
870 3
871 ])
872 deepStrictEqual(Arr.getSomes([Option.some(1), Option.none(), Option.some(3)]), [
873 1,
874 3
875 ])
876 })
877
878 it("separate", () => {
879 deepStrictEqual(Arr.separate([]), [[], []])
880 deepStrictEqual(Arr.separate([Result.succeed(1), Result.fail("e"), Result.fail(2), Result.succeed(2)]), [
881 [1, 2],
882 ["e", 2]
883 ])
884 })
885
886 it("filter", () => {
887 deepStrictEqual(Arr.filter([1, 2, 3], (n) => n % 2 === 1), [1, 3])
888 deepStrictEqual(Arr.filter([Option.some(3), Option.some(2), Option.some(1)], Option.isSome), [
889 Option.some(3),
890 Option.some(2),
891 Option.some(1)
892 ])
893 deepStrictEqual(Arr.filter([Option.some(3), Option.none(), Option.some(1)], Option.isSome), [
894 Option.some(3),
895 Option.some(1)
896 ])
897 deepStrictEqual(Arr.filter(["a", "b", "c"], (_, i) => i % 2 === 0), ["a", "c"])
898 })
899
900 it("filterMap", () => {
901 const f: (n: number, i: number) => Result.Result<number, string> = (n, i) =>
902 n + i < 4 ? Result.succeed(n * 2) : Result.fail(`skip: ${n}`)
903
904 deepStrictEqual(Arr.filterMap([], f), [])
905 deepStrictEqual(Arr.filterMap([1, 2, 3], f), [2, 4])
906 deepStrictEqual(pipe([1, 2, 3], Arr.filterMap(f)), [2, 4])
907 deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.filterMap(f)), [2, 4])
908 deepStrictEqual(pipe([1, 2, 3][Symbol.iterator](), Arr.filterMap(f)), [2, 4])
909 })
910
911 it("partition (identity)", () => {
912 deepStrictEqual(Arr.partition([], identity), [[], []])
913 deepStrictEqual(Arr.partition([Result.succeed(1), Result.fail("a"), Result.succeed(2)], identity), [[1, 2], ["a"]])
914 })
915
916 it("partition - transformed outputs", () => {
917 deepStrictEqual(Arr.partition([], (n) => n > 2 ? Result.succeed(n) : Result.fail(n)), [[], []])
918 deepStrictEqual(Arr.partition([1, 3], (n) => n > 2 ? Result.succeed(n) : Result.fail(n)), [[3], [1]])
919
920 deepStrictEqual(Arr.partition([], (n, i) => n + i > 2 ? Result.succeed(n) : Result.fail(n)), [[], []])
921 deepStrictEqual(Arr.partition([1, 2], (n, i) => n + i > 2 ? Result.succeed(n) : Result.fail(n)), [[2], [1]])
922 })
923
924 it("reduce", () => {
925 deepStrictEqual(pipe(["a", "b", "c"], Arr.reduce("", (b, a) => b + a)), "abc")
926 deepStrictEqual(
927 pipe(
928 ["a", "b"],
929 Arr.reduce("", (b, a, i) => b + i + a)
930 ),
931 "0a1b"
932 )
933 })
934
935 it("reduceRight", () => {
936 const f = (b: string, a: string) => b + a
937 deepStrictEqual(pipe(["a", "b", "c"], Arr.reduceRight("", f)), "cba")
938 deepStrictEqual(pipe([], Arr.reduceRight("", f)), "")
939 deepStrictEqual(
940 pipe(
941 ["a", "b"],
942 Arr.reduceRight("", (b, a, i) => b + i + a)
943 ),
944 "1b0a"
945 )
946 })
947
948 it("makeOrder", () => {
949 const O = Arr.makeOrder(Str.Order)
950 deepStrictEqual(O([], []), 0)
951 deepStrictEqual(O(["a"], ["a"]), 0)
952
953 deepStrictEqual(O(["a"], ["b"]), -1)
954 deepStrictEqual(O(["b"], ["a"]), 1)
955
956 deepStrictEqual(O([], ["a"]), -1)
957 deepStrictEqual(O(["a"], []), 1)
958 deepStrictEqual(O(["a"], ["a", "a"]), -1)
959 deepStrictEqual(O(["b"], ["a", "a"]), 1)
960
961 deepStrictEqual(O(["a", "a"], ["a", "a"]), 0)
962 deepStrictEqual(O(["a", "b"], ["a", "b"]), 0)
963
964 deepStrictEqual(O(["a", "b"], ["a", "a"]), 1)
965 deepStrictEqual(O(["a", "a"], ["a", "b"]), -1)
966
967 deepStrictEqual(O(["b", "a"], ["a", "b"]), 1)
968 deepStrictEqual(O(["a", "a"], ["b", "a"]), -1)
969 deepStrictEqual(O(["a", "b"], ["b", "a"]), -1)
970 deepStrictEqual(O(["b", "a"], ["b", "b"]), -1)
971 deepStrictEqual(O(["b", "b"], ["b", "a"]), 1)
972 })
973
974 it("isReadonlyArrayEmpty", () => {
975 deepStrictEqual(Arr.isReadonlyArrayEmpty([1, 2, 3]), false)
976 deepStrictEqual(Arr.isReadonlyArrayEmpty([]), true)
977 })
978
979 it("isArrayEmpty", () => {
980 deepStrictEqual(Arr.isArrayEmpty([1, 2, 3]), false)
981 deepStrictEqual(Arr.isArrayEmpty([]), true)
982 })
983
984 it("isReadonlyArrayNonEmpty", () => {
985 deepStrictEqual(Arr.isReadonlyArrayNonEmpty([1, 2, 3]), true)
986 deepStrictEqual(Arr.isReadonlyArrayNonEmpty([]), false)
987 })
988
989 it("isArrayNonEmpty", () => {
990 deepStrictEqual(Arr.isArrayNonEmpty([1, 2, 3]), true)
991 deepStrictEqual(Arr.isArrayNonEmpty([]), false)
992 })
993
994 it("head", () => {
995 const as: ReadonlyArray<number> = [1, 2, 3]
996 assertSome(Arr.head(as), 1)
997 assertNone(Arr.head([]))
998 })
999
1000 it("last", () => {
1001 const as: ReadonlyArray<number> = [1, 2, 3]
1002 assertSome(Arr.last(as), 3)
1003 assertNone(Arr.last([]))
1004 })
1005
1006 it("chunksOf", () => {
1007 deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5]), [
1008 Arr.make(1, 2),
1009 [3, 4],
1010 [5]
1011 ])
1012 deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5, 6]), [
1013 Arr.make(1, 2),
1014 [3, 4],
1015 [5, 6]
1016 ])
1017 deepStrictEqual(Arr.chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
1018 deepStrictEqual(Arr.chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]])
1019 // out of bounds
1020 deepStrictEqual(Arr.chunksOf(0)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
1021 deepStrictEqual(Arr.chunksOf(-1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
1022 deepStrictEqual(Arr.chunksOf(Number.NaN)([1, 2, 3]), [[1], [2], [3]])
1023
1024 const assertSingleChunk = (
1025 input: Arr.NonEmptyReadonlyArray<number>,
1026 n: number
1027 ) => {
1028 const chunks = Arr.chunksOf(n)(input)
1029 strictEqual(chunks.length, 1)
1030 deepStrictEqual(Arr.headNonEmpty(chunks), input)
1031 }
1032 // n = length
1033 assertSingleChunk(Arr.make(1, 2), 2)
1034 // n out of bounds
1035 assertSingleChunk(Arr.make(1, 2), 3)
1036 })
1037
1038 it("window", () => {
1039 deepStrictEqual(Arr.window([], 0), [])
1040 deepStrictEqual(Arr.window([], 2), [])
1041 deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 2), [[1, 2], [2, 3], [3, 4], [4, 5]])
1042 deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 3), [[1, 2, 3], [2, 3, 4], [3, 4, 5]])
1043 deepStrictEqual(Arr.window([1, 2, 3], 1.5), [[1], [2], [3]])
1044 deepStrictEqual(Arr.window([1, 2, 3], Number.NaN), [])
1045
1046 // n out of bounds
1047 deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 6), [])
1048 deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 0), [])
1049 deepStrictEqual(Arr.window([1, 2, 3, 4, 5], -1), [])
1050 })
1051
1052 it("min", () => {
1053 deepStrictEqual(Arr.min(Num.Order)([2, 1, 3]), 1)
1054 deepStrictEqual(Arr.min(Num.Order)([3]), 3)
1055 })
1056
1057 it("max", () => {
1058 deepStrictEqual(
1059 Arr.max(Num.Order)(Arr.make(1, 2, 3)),
1060 3
1061 )
1062 deepStrictEqual(Arr.max(Num.Order)([1]), 1)
1063 })
1064
1065 it("flatten", () => {
1066 deepStrictEqual(Arr.flatten([[1], [2], [3]]), [1, 2, 3])
1067 })
1068
1069 it("groupWith", () => {
1070 const groupWith = Arr.groupWith(Equivalence.strictEqual<number>())
1071 deepStrictEqual(groupWith([1, 2, 1, 1]), [[1], [2], [1, 1]])
1072 deepStrictEqual(groupWith([1, 2, 1, 1, 3]), [[1], [2], [1, 1], [3]])
1073 })
1074
1075 it("groupBy", () => {
1076 deepStrictEqual(Arr.groupBy((_) => "")([]), {})
1077 deepStrictEqual(Arr.groupBy((a) => `${a}`)([1]), { "1": [1] })
1078 deepStrictEqual(
1079 Arr.groupBy((s: string) => `${s.length}`)(["foo", "bar", "foobar"]),
1080 {
1081 "3": ["foo", "bar"],
1082 "6": ["foobar"]
1083 }
1084 )
1085 deepStrictEqual(Arr.groupBy(["a", "b"], (s) => s === "a" ? symA : s === "b" ? symB : symC), {
1086 [symA]: ["a"],
1087 [symB]: ["b"]
1088 })
1089 deepStrictEqual(Arr.groupBy(["a", "b", "c", "d"], (s) => s === "a" ? symA : s === "b" ? symB : symC), {
1090 [symA]: ["a"],
1091 [symB]: ["b"],
1092 [symC]: ["c", "d"]
1093 })
1094 })
1095
1096 it("match", () => {
1097 const len: <A>(as: ReadonlyArray<A>) => number = Arr.match({
1098 onEmpty: () => 0,
1099 onNonEmpty: (as) => 1 + len(as.slice(1))
1100 })
1101 deepStrictEqual(len([1, 2, 3]), 3)
1102 })
1103
1104 it("matchLeft", () => {
1105 const len: <A>(as: ReadonlyArray<A>) => number = Arr.matchLeft({
1106 onEmpty: () => 0,
1107 onNonEmpty: (_, tail) => 1 + len(tail)
1108 })
1109 deepStrictEqual(len([1, 2, 3]), 3)
1110 })
1111
1112 it("matchRight", () => {
1113 const len: <A>(as: ReadonlyArray<A>) => number = Arr.matchRight({
1114 onEmpty: () => 0,
1115 onNonEmpty: (init, _) => 1 + len(init)
1116 })
1117 deepStrictEqual(len([1, 2, 3]), 3)
1118 })
1119
1120 it("sortBy", () => {
1121 interface X {
1122 readonly a: string
1123 readonly b: number
1124 readonly c: boolean
1125 }
1126
1127 const byName = pipe(
1128 Str.Order,
1129 Order.mapInput((p: { readonly a: string; readonly b: number }) => p.a)
1130 )
1131
1132 const byAge = pipe(
1133 Num.Order,
1134 Order.mapInput((p: { readonly a: string; readonly b: number }) => p.b)
1135 )
1136
1137 const sortByNameByAge = Arr.sortBy(byName, byAge)
1138
1139 const xs: Arr.NonEmptyArray<X> = [
1140 { a: "a", b: 1, c: true },
1141 { a: "b", b: 3, c: true },
1142 { a: "c", b: 2, c: true },
1143 { a: "b", b: 2, c: true }
1144 ]
1145
1146 deepStrictEqual(Arr.sortBy()(xs), xs)
1147 deepStrictEqual(sortByNameByAge([]), [])
1148 deepStrictEqual(sortByNameByAge(xs), [
1149 { a: "a", b: 1, c: true },
1150 { a: "b", b: 2, c: true },
1151 { a: "b", b: 3, c: true },
1152 { a: "c", b: 2, c: true }
1153 ])
1154
1155 deepStrictEqual(Arr.sortBy()(new Set(xs)), xs)
1156 deepStrictEqual(sortByNameByAge(new Set([])), [])
1157 deepStrictEqual(sortByNameByAge(new Set(xs)), [
1158 { a: "a", b: 1, c: true },
1159 { a: "b", b: 2, c: true },
1160 { a: "b", b: 3, c: true },
1161 { a: "c", b: 2, c: true }
1162 ])
1163
1164 const sortByAgeByName = Arr.sortBy(byAge, byName)
1165 deepStrictEqual(sortByAgeByName(xs), [
1166 { a: "a", b: 1, c: true },
1167 { a: "b", b: 2, c: true },
1168 { a: "c", b: 2, c: true },
1169 { a: "b", b: 3, c: true }
1170 ])
1171 })
1172
1173 it("copy", () => {
1174 deepStrictEqual(pipe([], Arr.copy), [])
1175 deepStrictEqual(pipe([1, 2, 3], Arr.copy), [1, 2, 3])
1176 })
1177
1178 it("chop", () => {
1179 deepStrictEqual(pipe([], Arr.chop((as) => [as[0] * 2, as.slice(1)])), [])
1180 deepStrictEqual(pipe([1, 2, 3], Arr.chop((as) => [as[0] * 2, as.slice(1)])), [2, 4, 6])
1181 })
1182
1183 it("pad", () => {
1184 deepStrictEqual(pipe([], Arr.pad(0, 0)), [])
1185 deepStrictEqual(pipe([1, 2, 3], Arr.pad(0, 0)), [])
1186 deepStrictEqual(pipe([1, 2, 3], Arr.pad(2, 0)), [1, 2])
1187 deepStrictEqual(pipe([1], Arr.pad(1.5, 0)), [1])
1188 deepStrictEqual(pipe([1, 2, 3], Arr.pad(6, 0)), [1, 2, 3, 0, 0, 0])
1189 deepStrictEqual(pipe([1, 2, 3], Arr.pad(-2, 0)), [])
1190 deepStrictEqual(pipe([1, 2, 3], Arr.pad(Number.NaN, 0)), [])
1191 })
1192
1193 describe("chunksOf", () => {
1194 it("should split a `ReadonlyArray` into length-n pieces", () => {
1195 deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]])
1196 deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5, 6]), [
1197 [1, 2],
1198 [3, 4],
1199 [5, 6]
1200 ])
1201 deepStrictEqual(Arr.chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
1202 deepStrictEqual(Arr.chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]])
1203 // out of bounds
1204 deepStrictEqual(Arr.chunksOf(0)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
1205 deepStrictEqual(Arr.chunksOf(-1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
1206
1207 const assertSingleChunk = (input: ReadonlyArray<number>, n: number) => {
1208 const chunks = Arr.chunksOf(n)(input)
1209 deepStrictEqual(chunks.length, 1)
1210 deepStrictEqual(chunks[0], input)
1211 }
1212 // n = length
1213 assertSingleChunk([1, 2], 2)
1214 // n out of bounds
1215 assertSingleChunk([1, 2], 3)
1216 })
1217
1218 it("returns an empty array if provided an empty array", () => {
1219 const empty: ReadonlyArray<number> = []
1220 deepStrictEqual(Arr.chunksOf(0)(empty), Arr.empty())
1221 deepStrictEqual(Arr.chunksOf(0)(Arr.empty()), Arr.empty())
1222 deepStrictEqual(Arr.chunksOf(1)(empty), Arr.empty())
1223 deepStrictEqual(Arr.chunksOf(1)(Arr.empty()), Arr.empty())
1224 deepStrictEqual(Arr.chunksOf(2)(empty), Arr.empty())
1225 deepStrictEqual(Arr.chunksOf(2)(Arr.empty()), Arr.empty())
1226 })
1227
1228 it("should respect the law: chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys)))", () => {
1229 const xs: ReadonlyArray<number> = []
1230 const ys: ReadonlyArray<number> = [1, 2]
1231 deepStrictEqual(
1232 Arr.chunksOf(2)(xs).concat(Arr.chunksOf(2)(ys)),
1233 Arr.chunksOf(2)(xs.concat(ys))
1234 )
1235 fc.assert(
1236 fc.property(
1237 fc.array(fc.integer()).filter((xs) => xs.length % 2 === 0), // Ensures `xs.length` is even
1238 fc.array(fc.integer()),
1239 fc.integer({ min: 1, max: 1 }).map((x) => x * 2), // Generates `n` to be even so that it evenly divides `xs`
1240 (xs, ys, n) => {
1241 const as = Arr.chunksOf(n)(xs).concat(Arr.chunksOf(n)(ys))
1242 const bs = Arr.chunksOf(n)(xs.concat(ys))
1243 deepStrictEqual(as, bs)
1244 }
1245 )
1246 )
1247 })
1248 })
1249
1250 it("makeBy", () => {
1251 deepStrictEqual(Arr.makeBy(5, (n) => n * 2), [0, 2, 4, 6, 8])
1252 deepStrictEqual(Arr.makeBy((n) => n * 2)(5), [0, 2, 4, 6, 8])
1253 deepStrictEqual(Arr.makeBy(2.2, (n) => n * 2), [0, 2])
1254 deepStrictEqual(Arr.makeBy((n) => n * 2)(2.2), [0, 2])
1255 deepStrictEqual(Arr.makeBy(Number.NaN, (n) => n), [0])
1256 })
1257
1258 it("replicate", () => {
1259 deepStrictEqual(Arr.replicate("a", 0), ["a"])
1260 deepStrictEqual(Arr.replicate("a", -1), ["a"])
1261 deepStrictEqual(Arr.replicate("a", Number.NaN), ["a"])
1262 deepStrictEqual(Arr.replicate("a", 3), ["a", "a", "a"])
1263 deepStrictEqual(Arr.replicate("a", 2.2), ["a", "a"])
1264 })
1265
1266 it("range", () => {
1267 deepStrictEqual(Arr.range(0, 0), [0])
1268 deepStrictEqual(Arr.range(0, 1), [0, 1])
1269 deepStrictEqual(Arr.range(1, 5), [1, 2, 3, 4, 5])
1270 deepStrictEqual(Arr.range(10, 15), [10, 11, 12, 13, 14, 15])
1271 deepStrictEqual(Arr.range(-1, 0), [-1, 0])
1272 deepStrictEqual(Arr.range(-5, -1), [-5, -4, -3, -2, -1])
1273 // out of bound
1274 deepStrictEqual(Arr.range(2, 1), [2])
1275 deepStrictEqual(Arr.range(-1, -2), [-1])
1276 })
1277
1278 it("unionWith", () => {
1279 const two: ReadonlyArray<number> = [1, 2]
1280 deepStrictEqual(pipe(two, Arr.unionWith([3, 4], Equivalence.strictEqual<number>())), [1, 2, 3, 4])
1281 deepStrictEqual(pipe(two, Arr.unionWith([2, 3], Equivalence.strictEqual<number>())), [1, 2, 3])
1282 deepStrictEqual(pipe(two, Arr.unionWith([1, 2], Equivalence.strictEqual<number>())), [1, 2])
1283 deepStrictEqual(pipe(two, Arr.unionWith(Arr.empty(), Equivalence.strictEqual<number>())), two)
1284 deepStrictEqual(pipe(Arr.empty(), Arr.unionWith(two, Equivalence.strictEqual<number>())), two)
1285 deepStrictEqual(
1286 pipe(Arr.empty(), Arr.unionWith(Arr.empty(), Equivalence.strictEqual<number>())),
1287 Arr.empty()
1288 )
1289 })
1290
1291 it("intersectionWith", () => {
1292 const intersectionWith = Arr.intersectionWith(Equivalence.strictEqual<number>())
1293 deepStrictEqual(pipe([1, 2], intersectionWith([3, 4])), [])
1294 deepStrictEqual(pipe([1, 2], intersectionWith([2, 3])), [2])
1295 deepStrictEqual(pipe([1, 2], intersectionWith([1, 2])), [1, 2])
1296 // iterator input
1297 deepStrictEqual(pipe([1, 2], intersectionWith([3, 4][Symbol.iterator]())), [])
1298 deepStrictEqual(pipe([1, 2], intersectionWith([2, 3][Symbol.iterator]())), [2])
1299 deepStrictEqual(pipe([1, 2], intersectionWith([1, 2][Symbol.iterator]())), [1, 2])
1300 })
1301
1302 it("differenceWith", () => {
1303 const differenceWith = Arr.differenceWith(Equivalence.strictEqual<number>())
1304 deepStrictEqual(pipe([1, 2], differenceWith([3, 4])), [1, 2])
1305 deepStrictEqual(pipe([1, 2], differenceWith([2, 3])), [1])
1306 deepStrictEqual(pipe([1, 2], differenceWith([1, 2])), [])
1307 // iterator input
1308 deepStrictEqual(pipe([1, 2], differenceWith([3, 4][Symbol.iterator]())), [1, 2])
1309 deepStrictEqual(pipe([1, 2], differenceWith([2, 3][Symbol.iterator]())), [1])
1310 deepStrictEqual(pipe([1, 2], differenceWith([1, 2][Symbol.iterator]())), [])
1311 })
1312
1313 it("empty", () => {
1314 deepStrictEqual(Arr.empty.length, 0)
1315 })
1316
1317 it("every", () => {
1318 const isPositive: Predicate.Predicate<number> = (n) => n > 0
1319 deepStrictEqual(Arr.every([1, 2, 3], isPositive), true)
1320 deepStrictEqual(Arr.every([1, 2, -3], isPositive), false)
1321 })
1322
1323 it("some", () => {
1324 const isPositive: Predicate.Predicate<number> = (n) => n > 0
1325 deepStrictEqual(Arr.some([-1, -2, 3], isPositive), true)
1326 deepStrictEqual(Arr.some([-1, -2, -3], isPositive), false)
1327 })
1328
1329 it("length", () => {
1330 deepStrictEqual(Arr.length(Arr.empty()), 0)
1331 deepStrictEqual(Arr.length([]), 0)
1332 deepStrictEqual(Arr.length(["a"]), 1)
1333 })
1334
1335 it("fromOption", () => {
1336 deepStrictEqual(Arr.fromOption(Option.some("hello")), ["hello"])
1337 deepStrictEqual(Arr.fromOption(Option.none()), [])
1338 })
1339
1340 it("forEach", () => {
1341 const log: Array<string> = []
1342 Arr.forEach(["a", "b", "c"], (a, i) => log.push(`${a}-${i}`))
1343 deepStrictEqual(log, ["a-0", "b-1", "c-2"])
1344 })
1345
1346 it("sortWith", () => {
1347 type X = {
1348 a: string
1349 b: number
1350 }
1351 const arr: ReadonlyArray<X> = [{ a: "a", b: 2 }, { a: "b", b: 1 }]
1352 deepStrictEqual(Arr.sortWith(arr, (x) => x.b, Order.Number), [{ a: "b", b: 1 }, { a: "a", b: 2 }])
1353 })
1354
1355 it("countBy", () => {
1356 deepStrictEqual(Arr.countBy([1, 2, 3, 4, 5], (n) => n % 2 === 0), 2)
1357 deepStrictEqual(pipe([1, 2, 3, 4, 5], Arr.countBy((n) => n % 2 === 0)), 2)
1358 })
1359
1360 it("isArray", () => {
1361 strictEqual(Arr.isArray([]), true)
1362 strictEqual(Arr.isArray([1, 2, 3]), true)
1363 strictEqual(Arr.isArray("hello"), false)
1364 strictEqual(Arr.isArray(123), false)
1365 strictEqual(Arr.isArray(null), false)
1366 strictEqual(Arr.isArray(undefined), false)
1367 strictEqual(Arr.isArray({}), false)
1368 strictEqual(Arr.isArray(new Set([1, 2])), false)
1369 })
1370
1371 it("fromRecord", () => {
1372 deepStrictEqual(Arr.fromRecord({}), [])
1373 const entries = Arr.fromRecord({ a: 1, b: 2 })
1374 deepStrictEqual(entries.length, 2)
1375 deepStrictEqual(entries.sort(([a], [b]) => a.localeCompare(b)), [["a", 1], ["b", 2]])
1376 })
1377
1378 it("join", () => {
1379 deepStrictEqual(Arr.join([], ","), "")
1380 deepStrictEqual(Arr.join(["a"], ","), "a")
1381 deepStrictEqual(Arr.join(["a", "b", "c"], ", "), "a, b, c")
1382 deepStrictEqual(Arr.join(["a", "b", "c"], ""), "abc")
1383 deepStrictEqual(pipe(["x", "y"], Arr.join("-")), "x-y")
1384 })
1385
1386 it("mapAccum", () => {
1387 deepStrictEqual(Arr.mapAccum([1, 2, 3], 0, (acc, n) => [acc + n, acc + n]), [6, [1, 3, 6]])
1388 deepStrictEqual(Arr.mapAccum([], 10, (acc, n: number) => [acc + n, n]), [10, []])
1389 deepStrictEqual(
1390 pipe([1, 2, 3], Arr.mapAccum(0, (acc, n) => [acc + n, n * 2])),
1391 [6, [2, 4, 6]]
1392 )
1393 deepStrictEqual(
1394 Arr.mapAccum(["a", "b", "c"], "", (acc, s, i) => [acc + s, `${s}${i}`]),
1395 ["abc", ["a0", "b1", "c2"]]
1396 )
1397 })
1398
1399 it("partition", () => {
1400 const f: (n: number, i: number) => Result.Result<number, string> = (n, i) =>
1401 n > 0 ? Result.succeed(n + i) : Result.fail(`negative: ${n}:${i}`)
1402 deepStrictEqual(Arr.partition([], f), [[], []])
1403 deepStrictEqual(Arr.partition([1, -2, 3, -4], f), [[1, 5], ["negative: -2:1", "negative: -4:3"]])
1404 deepStrictEqual(pipe([5, 10], Arr.partition(f)), [[5, 11], []])
1405 deepStrictEqual(pipe([-1, -2], Arr.partition(f)), [[], ["negative: -1:0", "negative: -2:1"]])
1406 deepStrictEqual(
1407 pipe(new Set([1, -2, 3, -4]), Arr.partition(f)),
1408 [[1, 5], ["negative: -2:1", "negative: -4:3"]]
1409 )
1410 deepStrictEqual(pipe([1, -2, 3][Symbol.iterator](), Arr.partition(f)), [[1, 5], ["negative: -2:1"]])
1411 })
1412
1413 it("partition with typed pass/fail outputs", () => {
1414 const items: Array<string | number> = [1, "a", 2, "b"]
1415 deepStrictEqual(
1416 Arr.partition(items, (x) => typeof x === "number" ? Result.succeed(x) : Result.fail(x)),
1417 [[1, 2], ["a", "b"]]
1418 )
1419 })
1420
1421 it("getFailures", () => {
1422 deepStrictEqual(Arr.getFailures([]), [])
1423 deepStrictEqual(
1424 Arr.getFailures([Result.succeed(1), Result.fail("err"), Result.succeed(2)]),
1425 ["err"]
1426 )
1427 deepStrictEqual(
1428 Arr.getFailures([Result.fail("a"), Result.fail("b")]),
1429 ["a", "b"]
1430 )
1431 deepStrictEqual(
1432 Arr.getFailures([Result.succeed(1), Result.succeed(2)]),
1433 []
1434 )
1435 })
1436
1437 it("getSuccesses", () => {
1438 deepStrictEqual(Arr.getSuccesses([]), [])
1439 deepStrictEqual(
1440 Arr.getSuccesses([Result.succeed(1), Result.fail("err"), Result.succeed(2)]),
1441 [1, 2]
1442 )
1443 deepStrictEqual(
1444 Arr.getSuccesses([Result.fail("a"), Result.fail("b")]),
1445 []
1446 )
1447 deepStrictEqual(
1448 Arr.getSuccesses([Result.succeed(1), Result.succeed(2)]),
1449 [1, 2]
1450 )
1451 })
1452
1453 it("dedupe", () => {
1454 deepStrictEqual(Arr.dedupe([]), [])
1455 deepStrictEqual(Arr.dedupe([1, 2, 1, 3, 2, 4]), [1, 2, 3, 4])
1456 deepStrictEqual(Arr.dedupe([1, 2, 3]), [1, 2, 3])
1457 deepStrictEqual(Arr.dedupe([1, 1, 1]), [1])
1458 deepStrictEqual(Arr.dedupe(["a", "b", "a"]), ["a", "b"])
1459 deepStrictEqual(Arr.dedupe([NaN, NaN]), [NaN])
1460 deepStrictEqual(Arr.dedupe([0, -0]), [0])
1461 deepStrictEqual(
1462 Arr.dedupe([new HashCollision(1), new HashCollision(2), new HashCollision(1)]).map((value) => value.value),
1463 [1, 2]
1464 )
1465 const sparse = globalThis.Array<number>(2)
1466 sparse[1] = 1
1467 deepStrictEqual(Arr.dedupe(sparse), [undefined, 1])
1468 deepStrictEqual(Arr.dedupe(globalThis.Array(1)), [undefined])
1469 })
1470
1471 it("dedupe does not hash a single value", () => {
1472 let value: unknown = null
1473 for (let index = 0; index < 25_000; index++) {
1474 value = { value }
1475 }
1476 const result = Arr.dedupe([value])
1477 strictEqual(result.length, 1)
1478 strictEqual(result[0], value)
1479 })
1480
1481 it("dedupeAdjacent", () => {
1482 deepStrictEqual(Arr.dedupeAdjacent([]), [])
1483 deepStrictEqual(Arr.dedupeAdjacent([1, 2, 3]), [1, 2, 3])
1484 deepStrictEqual(Arr.dedupeAdjacent([1, 1, 2, 2, 3, 3]), [1, 2, 3])
1485 deepStrictEqual(Arr.dedupeAdjacent([1, 2, 1]), [1, 2, 1])
1486 deepStrictEqual(Arr.dedupeAdjacent([1, 1, 1]), [1])
1487 })
1488
1489 it("group", () => {
1490 deepStrictEqual(Arr.group([1, 1, 2, 2, 2, 3, 1]), [[1, 1], [2, 2, 2], [3], [1]])
1491 deepStrictEqual(Arr.group([1]), [[1]])
1492 deepStrictEqual(Arr.group(["a", "a", "b"]), [["a", "a"], ["b"]])
1493 })
1494
1495 it("union", () => {
1496 deepStrictEqual(Arr.union([1, 2], [2, 3]), [1, 2, 3])
1497 deepStrictEqual(Arr.union([], [1, 2]), [1, 2])
1498 deepStrictEqual(Arr.union([1, 2], []), [1, 2])
1499 deepStrictEqual(Arr.union([], []), [])
1500 deepStrictEqual(Arr.union([1, 2], [1, 2]), [1, 2])
1501 deepStrictEqual(pipe([1, 2], Arr.union([3, 4])), [1, 2, 3, 4])
1502 deepStrictEqual(
1503 Arr.union(
1504 [new HashCollision(1), new HashCollision(2)],
1505 [new HashCollision(1), new HashCollision(3)]
1506 ).map((value) => value.value),
1507 [1, 2, 3]
1508 )
1509 })
1510
1511 it("intersection", () => {
1512 deepStrictEqual(Arr.intersection([1, 2, 3], [3, 4, 1]), [1, 3])
1513 deepStrictEqual(Arr.intersection([1, 2], [3, 4]), [])
1514 deepStrictEqual(Arr.intersection([1, 2], [1, 2]), [1, 2])
1515 deepStrictEqual(Arr.intersection([], [1, 2]), [])
1516 deepStrictEqual(Arr.intersection([1, 2], []), [])
1517 deepStrictEqual(pipe([1, 2, 3], Arr.intersection([2, 3, 4])), [2, 3])
1518 deepStrictEqual(Arr.intersection([1, 1, 2], [1]), [1, 1])
1519 deepStrictEqual(
1520 Arr.intersection(
1521 [new HashCollision(1), new HashCollision(2)],
1522 [new HashCollision(1)]
1523 ).map((value) => value.value),
1524 [1]
1525 )
1526 })
1527
1528 it("difference", () => {
1529 deepStrictEqual(Arr.difference([1, 2, 3], [2, 3, 4]), [1])
1530 deepStrictEqual(Arr.difference([1, 2], [1, 2]), [])
1531 deepStrictEqual(Arr.difference([1, 2], []), [1, 2])
1532 deepStrictEqual(Arr.difference([], [1, 2]), [])
1533 deepStrictEqual(pipe([1, 2, 3], Arr.difference([3])), [1, 2])
1534 const sparse = globalThis.Array<number>(2)
1535 sparse[1] = 1
1536 deepStrictEqual(Arr.difference(sparse, []), [1])
1537 deepStrictEqual(
1538 Arr.difference(
1539 [new HashCollision(1), new HashCollision(2)],
1540 [new HashCollision(1)]
1541 ).map((value) => value.value),
1542 [2]
1543 )
1544 })
1545
1546 it("cartesianWith", () => {
1547 deepStrictEqual(
1548 Arr.cartesianWith([1, 2], ["a", "b"], (n, s) => `${n}-${s}`),
1549 ["1-a", "1-b", "2-a", "2-b"]
1550 )
1551 deepStrictEqual(Arr.cartesianWith([], ["a", "b"], (n: number, s) => `${n}-${s}`), [])
1552 deepStrictEqual(Arr.cartesianWith([1, 2], [], (n, s: string) => `${n}-${s}`), [])
1553 deepStrictEqual(
1554 pipe([1], Arr.cartesianWith(["x"], (n, s) => `${n}${s}`)),
1555 ["1x"]
1556 )
1557 })
1558
1559 it("cartesian", () => {
1560 deepStrictEqual(Arr.cartesian([1, 2], ["a", "b"]), [
1561 [1, "a"],
1562 [1, "b"],
1563 [2, "a"],
1564 [2, "b"]
1565 ])
1566 deepStrictEqual(Arr.cartesian([], ["a"]), [])
1567 deepStrictEqual(Arr.cartesian([1], []), [])
1568 deepStrictEqual(Arr.cartesian([1], ["a"]), [[1, "a"]])
1569 deepStrictEqual(pipe([1, 2], Arr.cartesian(["x"])), [[1, "x"], [2, "x"]])
1570 })
1571
1572 it("makeEquivalence", () => {
1573 const eq = Arr.makeEquivalence(Equivalence.strictEqual<number>())
1574 strictEqual(eq([], []), true)
1575 strictEqual(eq([1, 2, 3], [1, 2, 3]), true)
1576 strictEqual(eq([1, 2], [1, 2, 3]), false)
1577 strictEqual(eq([1, 2, 3], [1, 2]), false)
1578 strictEqual(eq([1, 2, 3], [1, 3, 2]), false)
1579 strictEqual(eq([1], [2]), false)
1580 })
1581
1582 it("getReadonlyReducerConcat", () => {
1583 const reducer = Arr.getReadonlyReducerConcat<number>()
1584 deepStrictEqual(reducer.combine([1, 2], [3, 4]), [1, 2, 3, 4])
1585 deepStrictEqual(reducer.combine([], [1, 2]), [1, 2])
1586 deepStrictEqual(reducer.combine([1, 2], []), [1, 2])
1587 deepStrictEqual(reducer.initialValue, [])
1588 })
1589
1590 it("makeReducerConcat", () => {
1591 const reducer = Arr.makeReducerConcat<string>()
1592 deepStrictEqual(reducer.combine(["a"], ["b"]), ["a", "b"])
1593 deepStrictEqual(reducer.initialValue, [])
1594 })
1595
1596 it("allocate", () => {
1597 deepStrictEqual(Arr.allocate(0).length, 0)
1598 deepStrictEqual(Arr.allocate(Number.NaN).length, 0)
1599 deepStrictEqual(Arr.allocate(-1).length, 0)
1600 deepStrictEqual(Arr.allocate(1.5).length, 1)
1601 deepStrictEqual(Arr.allocate(3).length, 3)
1602 })
1603
1604 describe("every - edge cases", () => {
1605 it("returns true for empty array", () => {
1606 strictEqual(Arr.every([], (_n: number) => false), true)
1607 })
1608 it("with index", () => {
1609 strictEqual(Arr.every([0, 1, 2], (n, i) => n === i), true)
1610 strictEqual(Arr.every([0, 1, 3], (n, i) => n === i), false)
1611 })
1612 })
1613
1614 describe("some - edge cases", () => {
1615 it("returns false for empty array", () => {
1616 strictEqual(Arr.some([], (_n: number) => true), false)
1617 })
1618 it("with index", () => {
1619 strictEqual(Arr.some([10, 20, 2], (n, i) => n === i), true)
1620 strictEqual(Arr.some([10, 20, 30], (n, i) => n === i), false)
1621 })
1622 })
1623
1624 describe("copy - edge cases", () => {
1625 it("returns a new reference", () => {
1626 const original = [1, 2, 3]
1627 const copied = Arr.copy(original)
1628 deepStrictEqual(copied, original)
1629 strictEqual(copied !== original, true)
1630 })
1631 })
1632
1633 describe("unfold - edge cases", () => {
1634 it("returns empty when seed produces Option.none", () => {
1635 deepStrictEqual(Arr.unfold(0, () => Option.none()), [])
1636 })
1637 })
1638
1639 it("bindTo", () => {
1640 deepStrictEqual(pipe([1, 2, 3], Arr.bindTo("a")), [{ a: 1 }, { a: 2 }, { a: 3 }])
1641 deepStrictEqual(pipe([], Arr.bindTo("a")), [])
1642 deepStrictEqual(Arr.bindTo([1], "x"), [{ x: 1 }])
1643 })
1644
1645 it("Do notation", () => {
1646 const _do = Arr.Do
1647 deepStrictEqual(_do, Arr.of({}))
1648
1649 const doA = Arr.bind(_do, "a", () => ["a"])
1650 deepStrictEqual(doA, Arr.of({ a: "a" }))
1651
1652 const doAB = Arr.bind(doA, "b", (x) => ["b", x.a + "b"])
1653 deepStrictEqual(doAB, [
1654 { a: "a", b: "b" },
1655 { a: "a", b: "ab" }
1656 ])
1657 const doABC = Arr.let(doAB, "c", (x) => [x.a, x.b, x.a + x.b])
1658 deepStrictEqual(doABC, [
1659 { a: "a", b: "b", c: ["a", "b", "ab"] },
1660 { a: "a", b: "ab", c: ["a", "ab", "aab"] }
1661 ])
1662
1663 const doABCD = Arr.bind(doABC, "d", () => Arr.empty())
1664 deepStrictEqual(doABCD, [])
1665
1666 const doAB__proto__C = pipe(
1667 Arr.let(doAB, "__proto__", (x) => [x.a, x.b, x.a + x.b]),
1668 Arr.let("c", (x) => [x.a, x.b, x.a + x.b])
1669 )
1670 deepStrictEqual(doAB__proto__C, [
1671 { a: "a", b: "b", c: ["a", "b", "ab"], ["__proto__"]: ["a", "b", "ab"] },
1672 { a: "a", b: "ab", c: ["a", "ab", "aab"], ["__proto__"]: ["a", "ab", "aab"] }
1673 ])
1674 })
1675})