import { describe, it } from "@effect/vitest" import { assertNone, assertSome, deepStrictEqual, strictEqual, throws } from "@effect/vitest/utils" import { Array as Arr, Equivalence, Number as Num, Option, Order, type Predicate, Result, String as Str } from "effect" import { identity, pipe } from "effect/Function" import { FastCheck as fc } from "effect/testing" const symA = Symbol.for("a") const symB = Symbol.for("b") const symC = Symbol.for("c") const double = (n: number) => n * 2 describe("Array", () => { it("of", () => { deepStrictEqual(Arr.of(1), [1]) }) it("fromIterable/Array should return the same reference if the iterable is an Array", () => { const i = [1, 2, 3] strictEqual(Arr.fromIterable(i), i) }) it("fromIterable/Iterable", () => { deepStrictEqual(Arr.fromIterable(new Set([1, 2, 3])), [1, 2, 3]) }) it("ensure", () => { deepStrictEqual(Arr.ensure(1), [1]) deepStrictEqual(Arr.ensure(null), [null]) deepStrictEqual(Arr.ensure([1]), [1]) deepStrictEqual(Arr.ensure([1, 2]), [1, 2]) deepStrictEqual(Arr.ensure(new Set([1, 2])), [new Set([1, 2])]) }) describe("iterable inputs", () => { it("prepend", () => { deepStrictEqual(pipe([1, 2, 3], Arr.prepend(0)), [0, 1, 2, 3]) deepStrictEqual(pipe([[2]], Arr.prepend([1])), [[1], [2]]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.prepend(0)), [0, 1, 2, 3]) deepStrictEqual(pipe(new Set([[2]]), Arr.prepend([1])), [[1], [2]]) }) it("prependAll", () => { deepStrictEqual(pipe([3, 4], Arr.prependAll([1, 2])), [1, 2, 3, 4]) deepStrictEqual(pipe([3, 4], Arr.prependAll(new Set([1, 2]))), [1, 2, 3, 4]) deepStrictEqual(pipe(new Set([3, 4]), Arr.prependAll([1, 2])), [1, 2, 3, 4]) }) it("append", () => { deepStrictEqual(pipe([1, 2, 3], Arr.append(4)), [1, 2, 3, 4]) deepStrictEqual(pipe([[1]], Arr.append([2])), [[1], [2]]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.append(4)), [1, 2, 3, 4]) deepStrictEqual(pipe(new Set([[1]]), Arr.append([2])), [[1], [2]]) }) it("appendAll", () => { deepStrictEqual(pipe([1, 2], Arr.appendAll([3, 4])), [1, 2, 3, 4]) deepStrictEqual(pipe([1, 2], Arr.appendAll(new Set([3, 4]))), [1, 2, 3, 4]) deepStrictEqual(pipe(new Set([1, 2]), Arr.appendAll([3, 4])), [1, 2, 3, 4]) }) it("scan", () => { const f = (b: number, a: number) => b - a deepStrictEqual(pipe([1, 2, 3], Arr.scan(10, f)), [10, 9, 7, 4]) deepStrictEqual(pipe([0], Arr.scan(10, f)), [10, 10]) deepStrictEqual(pipe([], Arr.scan(10, f)), [10]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.scan(10, f)), [10, 9, 7, 4]) deepStrictEqual(pipe(new Set([0]), Arr.scan(10, f)), [10, 10]) deepStrictEqual(pipe(new Set([]), Arr.scan(10, f)), [10]) }) it("scanRight", () => { const f = (b: number, a: number) => a - b deepStrictEqual(pipe([1, 2, 3], Arr.scanRight(10, f)), [-8, 9, -7, 10]) deepStrictEqual(pipe([0], Arr.scanRight(10, f)), [-10, 10]) deepStrictEqual(pipe([], Arr.scanRight(10, f)), [10]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.scanRight(10, f)), [-8, 9, -7, 10]) deepStrictEqual(pipe(new Set([0]), Arr.scanRight(10, f)), [-10, 10]) deepStrictEqual(pipe(new Set([]), Arr.scanRight(10, f)), [10]) }) it("tail", () => { assertSome(Arr.tail([1, 2, 3]), [2, 3]) assertNone(Arr.tail([])) assertSome(Arr.tail(new Set([1, 2, 3])), [2, 3]) assertNone(Arr.tail(new Set([]))) }) it("init", () => { assertSome(Arr.init([1, 2, 3]), [1, 2]) assertNone(Arr.init([])) assertSome(Arr.init(new Set([1, 2, 3])), [1, 2]) assertNone(Arr.init(new Set([]))) }) it("take", () => { deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(2)), [1, 2]) deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(0)), []) // out of bounds deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(-10)), []) deepStrictEqual(pipe([1, 2, 3, 4], Arr.take(10)), [1, 2, 3, 4]) deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(2)), [1, 2]) deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(0)), []) // out of bounds deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(-10)), []) deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.take(10)), [1, 2, 3, 4]) }) it("takeRight", () => { deepStrictEqual(pipe(Arr.empty(), Arr.takeRight(0)), []) deepStrictEqual(pipe([1, 2], Arr.takeRight(0)), []) deepStrictEqual(pipe([1, 2], Arr.takeRight(1)), [2]) deepStrictEqual(pipe([1, 2], Arr.takeRight(2)), [1, 2]) // out of bound deepStrictEqual(pipe(Arr.empty(), Arr.takeRight(1)), []) deepStrictEqual(pipe(Arr.empty(), Arr.takeRight(-1)), []) deepStrictEqual(pipe([1, 2], Arr.takeRight(3)), [1, 2]) deepStrictEqual(pipe([1, 2], Arr.takeRight(-1)), []) deepStrictEqual(pipe(new Set(), Arr.takeRight(0)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(0)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(1)), [2]) deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(2)), [1, 2]) // out of bound deepStrictEqual(pipe(new Set(), Arr.takeRight(1)), []) deepStrictEqual(pipe(new Set(), Arr.takeRight(-1)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(3)), [1, 2]) deepStrictEqual(pipe(new Set([1, 2]), Arr.takeRight(-1)), []) }) it("takeWhile", () => { const f = (n: number) => n % 2 === 0 deepStrictEqual(pipe([2, 4, 3, 6], Arr.takeWhile(f)), [2, 4]) deepStrictEqual(pipe(Arr.empty(), Arr.takeWhile(f)), []) deepStrictEqual(pipe([1, 2, 4], Arr.takeWhile(f)), []) deepStrictEqual(pipe([2, 4], Arr.takeWhile(f)), [2, 4]) deepStrictEqual(pipe(new Set([2, 4, 3, 6]), Arr.takeWhile(f)), [2, 4]) deepStrictEqual(pipe(new Set(), Arr.takeWhile(f)), []) deepStrictEqual(pipe(new Set([1, 2, 4]), Arr.takeWhile(f)), []) deepStrictEqual(pipe(new Set([2, 4]), Arr.takeWhile(f)), [2, 4]) }) it("takeWhileFilter", () => { const f: (n: number, i: number) => Result.Result = (n, i) => n + i < 5 ? Result.succeed(n * 10) : Result.fail(`stop: ${n}`) deepStrictEqual(Arr.takeWhileFilter([], f), []) deepStrictEqual(Arr.takeWhileFilter([1, 2, 3], f), [10, 20]) deepStrictEqual(pipe([1, 2, 3], Arr.takeWhileFilter(f)), [10, 20]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.takeWhileFilter(f)), [10, 20]) deepStrictEqual(pipe([][Symbol.iterator](), Arr.takeWhileFilter(f)), []) }) it("span", () => { const f = Arr.span((n) => n % 2 === 1) const assertSpan = ( input: Iterable, expectedInit: ReadonlyArray, expectedRest: ReadonlyArray ) => { const [init, rest] = f(input) deepStrictEqual(init, expectedInit) deepStrictEqual(rest, expectedRest) } assertSpan([1, 3, 2, 4, 5], [1, 3], [2, 4, 5]) assertSpan(Arr.empty(), Arr.empty(), Arr.empty()) assertSpan([1, 3], [1, 3], Arr.empty()) assertSpan([2, 4], Arr.empty(), [2, 4]) assertSpan(new Set([1, 3, 2, 4, 5]), [1, 3], [2, 4, 5]) assertSpan(new Set(), Arr.empty(), Arr.empty()) assertSpan(new Set([1, 3]), [1, 3], Arr.empty()) assertSpan(new Set([2, 4]), Arr.empty(), [2, 4]) // iterator input assertSpan([1, 3, 2, 4, 5][Symbol.iterator](), [1, 3], [2, 4, 5]) assertSpan([][Symbol.iterator](), Arr.empty(), Arr.empty()) }) it("splitWhere", () => { const f = Arr.splitWhere((n) => n % 2 !== 1) const assertSplitWhere = ( input: Iterable, expectedInit: ReadonlyArray, expectedRest: ReadonlyArray ) => { const [init, rest] = f(input) deepStrictEqual(init, expectedInit) deepStrictEqual(rest, expectedRest) } assertSplitWhere([1, 3, 2, 4, 5], [1, 3], [2, 4, 5]) assertSplitWhere(Arr.empty(), Arr.empty(), Arr.empty()) assertSplitWhere([1, 3], [1, 3], Arr.empty()) assertSplitWhere([2, 4], Arr.empty(), [2, 4]) assertSplitWhere(new Set([1, 3, 2, 4, 5]), [1, 3], [2, 4, 5]) assertSplitWhere(new Set(), Arr.empty(), Arr.empty()) assertSplitWhere(new Set([1, 3]), [1, 3], Arr.empty()) assertSplitWhere(new Set([2, 4]), Arr.empty(), [2, 4]) }) it("split", () => { deepStrictEqual(pipe(Arr.empty(), Arr.split(2)), Arr.empty()) deepStrictEqual(pipe(Arr.make(1), Arr.split(2)), Arr.make(Arr.make(1))) deepStrictEqual(pipe(Arr.make(1, 2), Arr.split(2)), Arr.make(Arr.make(1), Arr.make(2))) deepStrictEqual(pipe(Arr.make(1, 2, 3, 4, 5), Arr.split(2)), Arr.make(Arr.make(1, 2, 3), Arr.make(4, 5))) deepStrictEqual( pipe(Arr.make(1, 2, 3, 4, 5), Arr.split(3)), Arr.make(Arr.make(1, 2), Arr.make(3, 4), Arr.make(5)) ) }) it("drop", () => { deepStrictEqual(pipe(Arr.empty(), Arr.drop(0)), []) deepStrictEqual(pipe([1, 2], Arr.drop(0)), [1, 2]) deepStrictEqual(pipe([1, 2], Arr.drop(1)), [2]) deepStrictEqual(pipe([1, 2], Arr.drop(2)), []) // out of bound deepStrictEqual(pipe(Arr.empty(), Arr.drop(1)), []) deepStrictEqual(pipe(Arr.empty(), Arr.drop(-1)), []) deepStrictEqual(pipe([1, 2], Arr.drop(3)), []) deepStrictEqual(pipe([1, 2], Arr.drop(-1)), [1, 2]) deepStrictEqual(pipe(new Set(), Arr.drop(0)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(0)), [1, 2]) deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(1)), [2]) deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(2)), []) // out of bound deepStrictEqual(pipe(new Set(), Arr.drop(1)), []) deepStrictEqual(pipe(new Set(), Arr.drop(-1)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(3)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.drop(-1)), [1, 2]) }) it("dropRight", () => { deepStrictEqual(pipe([], Arr.dropRight(0)), []) deepStrictEqual(pipe([1, 2], Arr.dropRight(0)), [1, 2]) deepStrictEqual(pipe([1, 2], Arr.dropRight(1)), [1]) deepStrictEqual(pipe([1, 2], Arr.dropRight(2)), []) // out of bound deepStrictEqual(pipe([], Arr.dropRight(1)), []) deepStrictEqual(pipe([1, 2], Arr.dropRight(3)), []) deepStrictEqual(pipe([], Arr.dropRight(-1)), []) deepStrictEqual(pipe([1, 2], Arr.dropRight(-1)), [1, 2]) deepStrictEqual(pipe(new Set(), Arr.dropRight(0)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(0)), [1, 2]) deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(1)), [1]) deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(2)), []) // out of bound deepStrictEqual(pipe(new Set(), Arr.dropRight(1)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(3)), []) deepStrictEqual(pipe(new Set(), Arr.dropRight(-1)), []) deepStrictEqual(pipe(new Set([1, 2]), Arr.dropRight(-1)), [1, 2]) }) it("dropWhile", () => { const f = Arr.dropWhile((n) => n > 0) deepStrictEqual(f([]), []) deepStrictEqual(f([1, 2]), Arr.empty()) deepStrictEqual(f([-1, -2]), [-1, -2]) deepStrictEqual(f([-1, 2]), [-1, 2]) deepStrictEqual(f([1, -2, 3]), [-2, 3]) deepStrictEqual(f(new Set()), []) deepStrictEqual(f(new Set([1, 2])), Arr.empty()) deepStrictEqual(f(new Set([-1, -2])), [-1, -2]) deepStrictEqual(f(new Set([-1, 2])), [-1, 2]) deepStrictEqual(f(new Set([1, -2, 3])), [-2, 3]) // iterator input deepStrictEqual(f([1, -2, 3][Symbol.iterator]()), [-2, 3]) deepStrictEqual(f([][Symbol.iterator]()), []) }) it("dropWhileFilter", () => { const f: (n: number, i: number) => Result.Result = (n, i) => n + i < 5 ? Result.succeed(n * 10) : Result.fail(`stop: ${n}`) deepStrictEqual(Arr.dropWhileFilter([], f), []) deepStrictEqual(Arr.dropWhileFilter([1, 2, 3], f), [3]) deepStrictEqual(pipe([1, 2, 3], Arr.dropWhileFilter(f)), [3]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.dropWhileFilter(f)), [3]) deepStrictEqual(pipe([][Symbol.iterator](), Arr.dropWhileFilter(f)), []) }) it("findFirstIndex", () => { assertNone(pipe([], Arr.findFirstIndex((n) => n % 2 === 0))) assertSome(pipe([1, 2, 3], Arr.findFirstIndex((n) => n % 2 === 0)), 1) assertSome(pipe([1, 2, 3, 1], Arr.findFirstIndex((n) => n % 2 === 0)), 1) assertNone(pipe(new Set(), Arr.findFirstIndex((n) => n % 2 === 0))) assertSome(pipe(new Set([1, 2, 3]), Arr.findFirstIndex((n) => n % 2 === 0)), 1) assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findFirstIndex((n) => n % 2 === 0)), 1) }) it("findLastIndex", () => { assertNone(pipe([], Arr.findLastIndex((n) => n % 2 === 0))) assertSome(pipe([1, 2, 3], Arr.findLastIndex((n) => n % 2 === 0)), 1) assertSome(pipe([1, 2, 3, 4], Arr.findLastIndex((n) => n % 2 === 0)), 3) assertNone(pipe(new Set(), Arr.findLastIndex((n) => n % 2 === 0))) assertSome(pipe(new Set([1, 2, 3]), Arr.findLastIndex((n) => n % 2 === 0)), 1) assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findLastIndex((n) => n % 2 === 0)), 3) }) describe("findFirst", () => { it("boolean-returning overloads", () => { assertNone(pipe([], Arr.findFirst((n) => n % 2 === 0))) assertSome(pipe([1, 2, 3], Arr.findFirst((n) => n % 2 === 0)), 2) assertSome(pipe([1, 2, 3, 4], Arr.findFirst((n) => n % 2 === 0)), 2) assertNone(pipe(new Set(), Arr.findFirst((n) => n % 2 === 0))) assertSome(pipe(new Set([1, 2, 3]), Arr.findFirst((n) => n % 2 === 0)), 2) assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findFirst((n) => n % 2 === 0)), 2) }) it("Option-returning overloads", () => { assertNone( pipe([], Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())) ) assertSome( pipe([1, 2, 3], Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [2, 1] ) assertSome( pipe([1, 2, 3, 4], Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [2, 1] ) assertNone( pipe(new Set(), Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())) ) assertSome( pipe(new Set([1, 2, 3]), Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [2, 1] ) assertSome( pipe(new Set([1, 2, 3, 4]), Arr.findFirst((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [2, 1] ) }) }) describe("findFirstWithIndex", () => { it("boolean-returning overloads", () => { assertNone(pipe([], Arr.findFirstWithIndex((n) => n % 2 === 0))) assertSome(pipe([1, 2, 3], Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1]) assertSome(pipe([1, 2, 3, 4], Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1]) assertNone(pipe(new Set(), Arr.findFirstWithIndex((n) => n % 2 === 0))) assertSome(pipe(new Set([1, 2, 3]), Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1]) assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findFirstWithIndex((n) => n % 2 === 0)), [2, 1]) }) it("Option-returning overloads", () => { assertNone( pipe([], Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())) ) assertSome( pipe([1, 2, 3], Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())), [3, 1] ) assertSome( pipe([1, 2, 3, 4], Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())), [3, 1] ) assertNone( pipe(new Set(), Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())) ) assertSome( pipe(new Set([1, 2, 3]), Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())), [3, 1] ) assertSome( pipe(new Set([1, 2, 3, 4]), Arr.findFirstWithIndex((n) => n % 2 === 0 ? Option.some(n + 1) : Option.none())), [3, 1] ) }) }) describe("findLast", () => { it("boolean-returning overloads", () => { assertNone(pipe([], Arr.findLast((n) => n % 2 === 0))) assertSome(pipe([1, 2, 3], Arr.findLast((n) => n % 2 === 0)), 2) assertSome(pipe([1, 2, 3, 4], Arr.findLast((n) => n % 2 === 0)), 4) assertNone(pipe(new Set(), Arr.findLast((n) => n % 2 === 0))) assertSome(pipe(new Set([1, 2, 3]), Arr.findLast((n) => n % 2 === 0)), 2) assertSome(pipe(new Set([1, 2, 3, 4]), Arr.findLast((n) => n % 2 === 0)), 4) }) it("Option-returning overloads", () => { assertNone( pipe([], Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())) ) assertSome( pipe([1, 2, 3], Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [2, 1] ) assertSome( pipe([1, 2, 3, 4], Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [4, 3] ) assertNone( pipe(new Set(), Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())) ) assertSome( pipe(new Set([1, 2, 3]), Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [2, 1] ) assertSome( pipe(new Set([1, 2, 3, 4]), Arr.findLast((n, i) => n % 2 === 0 ? Option.some([n, i]) : Option.none())), [4, 3] ) }) }) it("insertAt", () => { assertNone(Arr.insertAt(1, 1)([])) assertSome(Arr.insertAt(0, 1)([]), [1]) assertSome(Arr.insertAt(2, 5)([1, 2, 3, 4]), [1, 2, 5, 3, 4]) // out of bound assertNone(Arr.insertAt(-1, 5)([1, 2, 3, 4])) assertNone(Arr.insertAt(10, 5)([1, 2, 3, 4])) assertNone(Arr.insertAt(1, 1)(new Set([]))) assertSome(Arr.insertAt(0, 1)(new Set([])), [1]) assertSome(Arr.insertAt(2, 5)(new Set([1, 2, 3, 4])), [1, 2, 5, 3, 4]) // out of bound assertNone(Arr.insertAt(-1, 5)(new Set([1, 2, 3, 4]))) assertNone(Arr.insertAt(10, 5)(new Set([1, 2, 3, 4]))) }) it("replace", () => { assertSome(pipe([1, 2, 3], Arr.replace(1, "a")), [1, "a", 3]) // out of bound assertNone(pipe([], Arr.replace(1, "a"))) assertNone(pipe([1, 2, 3], Arr.replace(-1, "a"))) assertNone(pipe([1, 2, 3], Arr.replace(10, "a"))) assertSome(pipe(new Set([1, 2, 3]), Arr.replace(1, "a")), [1, "a", 3]) // out of bound assertNone(pipe(new Set([]), Arr.replace(1, "a"))) assertNone(pipe(new Set([1, 2, 3]), Arr.replace(-1, "a"))) assertNone(pipe(new Set([1, 2, 3]), Arr.replace(10, "a"))) }) it("modify", () => { assertSome(pipe([1, 2, 3], Arr.modify(1, double)), [1, 4, 3]) // out of bound assertNone(pipe([], Arr.modify(1, double))) assertNone(pipe([1, 2, 3], Arr.modify(10, double))) assertSome(pipe(new Set([1, 2, 3]), Arr.modify(1, double)), [1, 4, 3]) // out of bound assertNone(pipe(new Set([]), Arr.modify(1, double))) assertNone(pipe(new Set([1, 2, 3]), Arr.modify(10, double))) }) it("remove", () => { deepStrictEqual(pipe([1, 2, 3], Arr.remove(0)), [2, 3]) // out of bound deepStrictEqual(pipe([], Arr.remove(0)), []) deepStrictEqual(pipe([1, 2, 3], Arr.remove(-1)), [1, 2, 3]) deepStrictEqual(pipe([1, 2, 3], Arr.remove(10)), [1, 2, 3]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.remove(0)), [2, 3]) // out of bound deepStrictEqual(pipe(new Set([]), Arr.remove(0)), []) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.remove(-1)), [1, 2, 3]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.remove(10)), [1, 2, 3]) }) it("reverse", () => { deepStrictEqual(Arr.reverse([]), []) deepStrictEqual(Arr.reverse([1]), [1]) deepStrictEqual(Arr.reverse([1, 2, 3]), [3, 2, 1]) deepStrictEqual(Arr.reverse(new Set([])), []) deepStrictEqual(Arr.reverse(new Set([1])), [1]) deepStrictEqual(Arr.reverse(new Set([1, 2, 3])), [3, 2, 1]) }) it("sort", () => { deepStrictEqual(Arr.sort(Num.Order)([]), []) deepStrictEqual(Arr.sort(Num.Order)([1, 3, 2]), [1, 2, 3]) deepStrictEqual(Arr.sort(Num.Order)(new Set()), []) deepStrictEqual(Arr.sort(Num.Order)(new Set([1, 3, 2])), [1, 2, 3]) }) it("zip", () => { deepStrictEqual(pipe(new Set([]), Arr.zip(new Set(["a", "b", "c", "d"]))), []) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.zip(new Set([]))), []) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.zip(new Set(["a", "b", "c", "d"]))), [ [1, "a"], [2, "b"], [3, "c"] ]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.zip(new Set(["a", "b", "c", "d"]))), [ [1, "a"], [2, "b"], [3, "c"] ]) }) it("zipWith", () => { deepStrictEqual( pipe(new Set([1, 2, 3]), Arr.zipWith(new Set([]), (n, s) => s + n)), [] ) deepStrictEqual( pipe(new Set([]), Arr.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n)), [] ) deepStrictEqual( pipe(new Set([]), Arr.zipWith(new Set([]), (n, s) => s + n)), [] ) deepStrictEqual( pipe(new Set([1, 2, 3]), Arr.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n)), ["a1", "b2", "c3"] ) }) it("unzip", () => { deepStrictEqual(Arr.unzip(new Set([])), [[], []]) deepStrictEqual( Arr.unzip( new Set( [ [1, "a"], [2, "b"], [3, "c"] ] as const ) ), [ [1, 2, 3], ["a", "b", "c"] ] ) }) it("intersperse", () => { deepStrictEqual(pipe([], Arr.intersperse(0)), []) deepStrictEqual(pipe([1], Arr.intersperse(0)), [1]) deepStrictEqual(pipe([1, 2, 3], Arr.intersperse(0)), [1, 0, 2, 0, 3]) deepStrictEqual(pipe([1, 2], Arr.intersperse(0)), [1, 0, 2]) deepStrictEqual(pipe([1, 2, 3, 4], Arr.intersperse(0)), [1, 0, 2, 0, 3, 0, 4]) deepStrictEqual(pipe(new Set([]), Arr.intersperse(0)), []) deepStrictEqual(pipe(new Set([1]), Arr.intersperse(0)), [1]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.intersperse(0)), [1, 0, 2, 0, 3]) deepStrictEqual(pipe(new Set([1, 2]), Arr.intersperse(0)), [1, 0, 2]) deepStrictEqual(pipe(new Set([1, 2, 3, 4]), Arr.intersperse(0)), [1, 0, 2, 0, 3, 0, 4]) }) it("rotate", () => { deepStrictEqual(Arr.rotate(0)(Arr.empty()), Arr.empty()) deepStrictEqual(Arr.rotate(1)(Arr.empty()), Arr.empty()) deepStrictEqual(Arr.rotate(1)([1]), [1]) deepStrictEqual(Arr.rotate(2)([1]), [1]) deepStrictEqual(Arr.rotate(-1)([1]), [1]) deepStrictEqual(Arr.rotate(-2)([1]), [1]) deepStrictEqual(Arr.rotate(2)([1, 2]), [1, 2]) deepStrictEqual(Arr.rotate(0)([1, 2]), [1, 2]) deepStrictEqual(Arr.rotate(-2)([1, 2]), [1, 2]) deepStrictEqual(Arr.rotate(1)([1, 2]), [2, 1]) deepStrictEqual(Arr.rotate(1)(new Set([1, 2, 3, 4, 5])), [5, 1, 2, 3, 4]) deepStrictEqual(Arr.rotate(2)(new Set([1, 2, 3, 4, 5])), [4, 5, 1, 2, 3]) deepStrictEqual(Arr.rotate(-1)(new Set([1, 2, 3, 4, 5])), [2, 3, 4, 5, 1]) deepStrictEqual(Arr.rotate(-2)(new Set([1, 2, 3, 4, 5])), [3, 4, 5, 1, 2]) // out of bounds deepStrictEqual(Arr.rotate(7)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) deepStrictEqual(Arr.rotate(-7)([1, 2, 3, 4, 5]), [3, 4, 5, 1, 2]) deepStrictEqual(Arr.rotate(2.2)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) deepStrictEqual(Arr.rotate(-2.2)([1, 2, 3, 4, 5]), [3, 4, 5, 1, 2]) }) it("containsWith", () => { const contains = Arr.containsWith(Equivalence.strictEqual()) deepStrictEqual(pipe([1, 2, 3], contains(2)), true) deepStrictEqual(pipe([1, 2, 3], contains(0)), false) deepStrictEqual(pipe(new Set([1, 2, 3]), contains(2)), true) deepStrictEqual(pipe(new Set([1, 2, 3]), contains(0)), false) }) it("contains", () => { const contains = Arr.contains deepStrictEqual(pipe([1, 2, 3], contains(2)), true) deepStrictEqual(pipe([1, 2, 3], contains(0)), false) deepStrictEqual(pipe(new Set([1, 2, 3]), contains(2)), true) deepStrictEqual(pipe(new Set([1, 2, 3]), contains(0)), false) }) it("dedupeWith", () => { const dedupe = Arr.dedupeWith(Equivalence.strictEqual()) deepStrictEqual(dedupe([]), []) deepStrictEqual(dedupe([-0, -0]), [-0]) deepStrictEqual(dedupe([0, -0]), [0]) deepStrictEqual(dedupe([1]), [1]) deepStrictEqual(dedupe([2, 1, 2]), [2, 1]) deepStrictEqual(dedupe([1, 2, 1]), [1, 2]) deepStrictEqual(dedupe([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) deepStrictEqual(dedupe([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]), [1, 2, 3, 4, 5]) deepStrictEqual(dedupe([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) }) it("dedupeAdjacentWith", () => { const dedupeAdjacent = Arr.dedupeAdjacentWith(Equivalence.strictEqual()) deepStrictEqual(dedupeAdjacent([]), []) deepStrictEqual(dedupeAdjacent([1, 2, 3]), [1, 2, 3]) deepStrictEqual(dedupeAdjacent([1, 2, 2, 3, 3]), [1, 2, 3]) }) it("splitAt", () => { const assertSplitAt = ( input: ReadonlyArray, index: number, expectedInit: ReadonlyArray, expectedRest: ReadonlyArray ) => { const [init, rest] = Arr.splitAt(index)(input) deepStrictEqual(init, expectedInit) deepStrictEqual(rest, expectedRest) } deepStrictEqual(Arr.splitAt(1)([1, 2]), [[1], [2]]) assertSplitAt([1, 2], 2, [1, 2], []) deepStrictEqual(Arr.splitAt(2)([1, 2, 3, 4, 5]), [ [1, 2], [3, 4, 5] ]) deepStrictEqual(Arr.splitAt(2)(new Set([1, 2, 3, 4, 5])), [ [1, 2], [3, 4, 5] ]) assertSplitAt([], 0, [], []) assertSplitAt([1, 2], 0, [], [1, 2]) // out of bounds assertSplitAt([], -1, [], []) assertSplitAt([1, 2], -1, [], [1, 2]) assertSplitAt([1, 2], 3, [1, 2], []) assertSplitAt([], 3, [], []) }) }) it("splitAtNonEmpty", () => { deepStrictEqual(pipe(Arr.make(1, 2, 3, 4), Arr.splitAtNonEmpty(2)), [[1, 2], [3, 4]]) deepStrictEqual(pipe(Arr.make(1, 2, 3, 4), Arr.splitAtNonEmpty(10)), [[1, 2, 3, 4], []]) }) describe("getUnsafe", () => { it("should throw on index out of bound", () => { throws(() => pipe([], Arr.getUnsafe(100)), new Error("Index out of bounds: 100")) }) }) it("fromNullishOr", () => { deepStrictEqual(Arr.fromNullishOr(undefined), []) deepStrictEqual(Arr.fromNullishOr(null), []) deepStrictEqual(Arr.fromNullishOr(1), [1]) }) it("liftNullishOr", () => { const f = Arr.liftNullishOr((n: number) => (n > 0 ? n : null)) deepStrictEqual(f(1), [1]) deepStrictEqual(f(-1), []) }) it("flatMapNullishOr", () => { const f = Arr.flatMapNullishOr((n: number) => (n > 0 ? n : null)) deepStrictEqual(pipe([], f), []) deepStrictEqual(pipe([1], f), [1]) deepStrictEqual(pipe([1, 2], f), [1, 2]) deepStrictEqual(pipe([-1], f), []) deepStrictEqual(pipe([-1, 2], f), [2]) }) it("liftPredicate", () => { const p = (n: number): boolean => n > 2 const f = Arr.liftPredicate(p) deepStrictEqual(f(1), []) deepStrictEqual(f(3), [3]) }) it("liftOption", () => { const f = Arr.liftOption((n: number) => (n > 0 ? Option.some(n) : Option.none())) deepStrictEqual(f(1), [1]) deepStrictEqual(f(-1), []) }) it("unprepend", () => { deepStrictEqual(Arr.unprepend([0]), [0, []]) deepStrictEqual(Arr.unprepend([1, 2, 3, 4]), [1, [2, 3, 4]]) }) it("unappend", () => { deepStrictEqual(Arr.unappend([0]), [[], 0]) deepStrictEqual(Arr.unappend([1, 2, 3, 4]), [ Arr.make(1, 2, 3), 4 ]) deepStrictEqual(Arr.unappend([0]), [[], 0]) deepStrictEqual(Arr.unappend([1, 2, 3, 4]), [ Arr.make(1, 2, 3), 4 ]) }) it("modifyHeadNonEmpty", () => { const f = (s: string) => s + "!" deepStrictEqual(pipe(["a"], Arr.modifyHeadNonEmpty(f)), ["a!"]) deepStrictEqual(pipe(["a", "b"], Arr.modifyHeadNonEmpty(f)), ["a!", "b"]) deepStrictEqual(pipe(["a", "b", "c"], Arr.modifyHeadNonEmpty(f)), ["a!", "b", "c"]) }) it("modifyLastNonEmpty", () => { const f = (s: string) => s + "!" deepStrictEqual(pipe(["a"], Arr.modifyLastNonEmpty(f)), ["a!"]) deepStrictEqual(pipe(["a", "b"], Arr.modifyLastNonEmpty(f)), ["a", "b!"]) deepStrictEqual(pipe(["a", "b", "c"], Arr.modifyLastNonEmpty(f)), ["a", "b", "c!"]) }) it("setHeadNonEmpty", () => { deepStrictEqual(pipe(Arr.make("a"), Arr.setHeadNonEmpty("d")), ["d"]) deepStrictEqual(pipe(Arr.make("a", "b"), Arr.setHeadNonEmpty("d")), ["d", "b"]) deepStrictEqual(pipe(Arr.make("a", "b", "c"), Arr.setHeadNonEmpty("d")), ["d", "b", "c"]) }) it("setLastNonEmpty", () => { deepStrictEqual(pipe(Arr.make("a"), Arr.setLastNonEmpty("d")), ["d"]) deepStrictEqual(pipe(Arr.make("a", "b"), Arr.setLastNonEmpty("d")), ["a", "d"]) deepStrictEqual(pipe(Arr.make("a", "b", "c"), Arr.setLastNonEmpty("d")), ["a", "b", "d"]) }) it("liftResult", () => { const f = Arr.liftResult((s: string) => s.length > 2 ? Result.succeed(s.length) : Result.fail("e")) deepStrictEqual(f("a"), []) deepStrictEqual(f("aaa"), [3]) }) it("headNonEmpty", () => { deepStrictEqual(Arr.headNonEmpty(Arr.make(1, 2)), 1) }) it("tailNonEmpty", () => { deepStrictEqual(Arr.tailNonEmpty(Arr.make(1, 2)), [2]) }) it("lastNonEmpty", () => { deepStrictEqual(Arr.lastNonEmpty(Arr.make(1, 2, 3)), 3) deepStrictEqual(Arr.lastNonEmpty([1]), 1) }) it("initNonEmpty", () => { deepStrictEqual( Arr.initNonEmpty(Arr.make(1, 2, 3)), Arr.make(1, 2) ) deepStrictEqual(Arr.initNonEmpty([1]), []) }) it("get", () => { assertSome(pipe([1, 2, 3], Arr.get(0)), 1) assertNone(pipe([1, 2, 3], Arr.get(3))) }) it("unfold", () => { const as = Arr.unfold(5, (n) => (n > 0 ? Option.some([n, n - 1]) : Option.none())) deepStrictEqual(as, [5, 4, 3, 2, 1]) }) it("map", () => { deepStrictEqual( pipe([1, 2, 3], Arr.map((n) => n * 2)), [2, 4, 6] ) deepStrictEqual( pipe(["a", "b"], Arr.map((s, i) => s + i)), ["a0", "b1"] ) }) it("flatMap", () => { deepStrictEqual( pipe([1, 2, 3], Arr.flatMap((n) => [n, n + 1])), [1, 2, 2, 3, 3, 4] ) const f = Arr.flatMap((n: number, i) => [n + i]) deepStrictEqual(pipe([], f), []) deepStrictEqual(pipe([1, 2, 3], f), [1, 3, 5]) }) it("extend", () => { deepStrictEqual(pipe([1, 2, 3, 4], Arr.extend(Num.sumAll)), [10, 9, 7, 4]) deepStrictEqual(pipe([1, 2, 3, 4], Arr.extend(identity)), [ [1, 2, 3, 4], [2, 3, 4], [3, 4], [4] ]) }) it("compact", () => { deepStrictEqual(Arr.getSomes([]), []) deepStrictEqual(Arr.getSomes([Option.some(1), Option.some(2), Option.some(3)]), [ 1, 2, 3 ]) deepStrictEqual(Arr.getSomes([Option.some(1), Option.none(), Option.some(3)]), [ 1, 3 ]) }) it("separate", () => { deepStrictEqual(Arr.separate([]), [[], []]) deepStrictEqual(Arr.separate([Result.succeed(1), Result.fail("e"), Result.fail(2), Result.succeed(2)]), [ ["e", 2], [1, 2] ]) }) it("filter", () => { deepStrictEqual(Arr.filter([1, 2, 3], (n) => n % 2 === 1), [1, 3]) deepStrictEqual(Arr.filter([Option.some(3), Option.some(2), Option.some(1)], Option.isSome), [ Option.some(3), Option.some(2), Option.some(1) ]) deepStrictEqual(Arr.filter([Option.some(3), Option.none(), Option.some(1)], Option.isSome), [ Option.some(3), Option.some(1) ]) deepStrictEqual(Arr.filter(["a", "b", "c"], (_, i) => i % 2 === 0), ["a", "c"]) }) it("filterMap", () => { const f: (n: number, i: number) => Result.Result = (n, i) => n + i < 4 ? Result.succeed(n * 2) : Result.fail(`skip: ${n}`) deepStrictEqual(Arr.filterMap([], f), []) deepStrictEqual(Arr.filterMap([1, 2, 3], f), [2, 4]) deepStrictEqual(pipe([1, 2, 3], Arr.filterMap(f)), [2, 4]) deepStrictEqual(pipe(new Set([1, 2, 3]), Arr.filterMap(f)), [2, 4]) deepStrictEqual(pipe([1, 2, 3][Symbol.iterator](), Arr.filterMap(f)), [2, 4]) }) it("partition (identity)", () => { deepStrictEqual(Arr.partition([], identity), [[], []]) deepStrictEqual(Arr.partition([Result.succeed(1), Result.fail("a"), Result.succeed(2)], identity), [["a"], [1, 2]]) }) it("partition - transformed outputs", () => { deepStrictEqual(Arr.partition([], (n) => n > 2 ? Result.succeed(n) : Result.fail(n)), [[], []]) deepStrictEqual(Arr.partition([1, 3], (n) => n > 2 ? Result.succeed(n) : Result.fail(n)), [[1], [3]]) deepStrictEqual(Arr.partition([], (n, i) => n + i > 2 ? Result.succeed(n) : Result.fail(n)), [[], []]) deepStrictEqual(Arr.partition([1, 2], (n, i) => n + i > 2 ? Result.succeed(n) : Result.fail(n)), [[1], [2]]) }) it("reduce", () => { deepStrictEqual(pipe(["a", "b", "c"], Arr.reduce("", (b, a) => b + a)), "abc") deepStrictEqual( pipe( ["a", "b"], Arr.reduce("", (b, a, i) => b + i + a) ), "0a1b" ) }) it("reduceRight", () => { const f = (b: string, a: string) => b + a deepStrictEqual(pipe(["a", "b", "c"], Arr.reduceRight("", f)), "cba") deepStrictEqual(pipe([], Arr.reduceRight("", f)), "") deepStrictEqual( pipe( ["a", "b"], Arr.reduceRight("", (b, a, i) => b + i + a) ), "1b0a" ) }) it("makeOrder", () => { const O = Arr.makeOrder(Str.Order) deepStrictEqual(O([], []), 0) deepStrictEqual(O(["a"], ["a"]), 0) deepStrictEqual(O(["a"], ["b"]), -1) deepStrictEqual(O(["b"], ["a"]), 1) deepStrictEqual(O([], ["a"]), -1) deepStrictEqual(O(["a"], []), 1) deepStrictEqual(O(["a"], ["a", "a"]), -1) deepStrictEqual(O(["b"], ["a", "a"]), 1) deepStrictEqual(O(["a", "a"], ["a", "a"]), 0) deepStrictEqual(O(["a", "b"], ["a", "b"]), 0) deepStrictEqual(O(["a", "b"], ["a", "a"]), 1) deepStrictEqual(O(["a", "a"], ["a", "b"]), -1) deepStrictEqual(O(["b", "a"], ["a", "b"]), 1) deepStrictEqual(O(["a", "a"], ["b", "a"]), -1) deepStrictEqual(O(["a", "b"], ["b", "a"]), -1) deepStrictEqual(O(["b", "a"], ["b", "b"]), -1) deepStrictEqual(O(["b", "b"], ["b", "a"]), 1) }) it("isReadonlyArrayEmpty", () => { deepStrictEqual(Arr.isReadonlyArrayEmpty([1, 2, 3]), false) deepStrictEqual(Arr.isReadonlyArrayEmpty([]), true) }) it("isArrayEmpty", () => { deepStrictEqual(Arr.isArrayEmpty([1, 2, 3]), false) deepStrictEqual(Arr.isArrayEmpty([]), true) }) it("isReadonlyArrayNonEmpty", () => { deepStrictEqual(Arr.isReadonlyArrayNonEmpty([1, 2, 3]), true) deepStrictEqual(Arr.isReadonlyArrayNonEmpty([]), false) }) it("isArrayNonEmpty", () => { deepStrictEqual(Arr.isArrayNonEmpty([1, 2, 3]), true) deepStrictEqual(Arr.isArrayNonEmpty([]), false) }) it("head", () => { const as: ReadonlyArray = [1, 2, 3] assertSome(Arr.head(as), 1) assertNone(Arr.head([])) }) it("last", () => { const as: ReadonlyArray = [1, 2, 3] assertSome(Arr.last(as), 3) assertNone(Arr.last([])) }) it("chunksOf", () => { deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5]), [ Arr.make(1, 2), [3, 4], [5] ]) deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5, 6]), [ Arr.make(1, 2), [3, 4], [5, 6] ]) deepStrictEqual(Arr.chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) deepStrictEqual(Arr.chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]]) // out of bounds deepStrictEqual(Arr.chunksOf(0)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) deepStrictEqual(Arr.chunksOf(-1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) const assertSingleChunk = ( input: Arr.NonEmptyReadonlyArray, n: number ) => { const chunks = Arr.chunksOf(n)(input) strictEqual(chunks.length, 1) deepStrictEqual(Arr.headNonEmpty(chunks), input) } // n = length assertSingleChunk(Arr.make(1, 2), 2) // n out of bounds assertSingleChunk(Arr.make(1, 2), 3) }) it("window", () => { deepStrictEqual(Arr.window([], 0), []) deepStrictEqual(Arr.window([], 2), []) deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 2), [[1, 2], [2, 3], [3, 4], [4, 5]]) deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 3), [[1, 2, 3], [2, 3, 4], [3, 4, 5]]) // n out of bounds deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 6), []) deepStrictEqual(Arr.window([1, 2, 3, 4, 5], 0), []) deepStrictEqual(Arr.window([1, 2, 3, 4, 5], -1), []) }) it("min", () => { deepStrictEqual(Arr.min(Num.Order)([2, 1, 3]), 1) deepStrictEqual(Arr.min(Num.Order)([3]), 3) }) it("max", () => { deepStrictEqual( Arr.max(Num.Order)(Arr.make(1, 2, 3)), 3 ) deepStrictEqual(Arr.max(Num.Order)([1]), 1) }) it("flatten", () => { deepStrictEqual(Arr.flatten([[1], [2], [3]]), [1, 2, 3]) }) it("groupWith", () => { const groupWith = Arr.groupWith(Equivalence.strictEqual()) deepStrictEqual(groupWith([1, 2, 1, 1]), [[1], [2], [1, 1]]) deepStrictEqual(groupWith([1, 2, 1, 1, 3]), [[1], [2], [1, 1], [3]]) }) it("groupBy", () => { deepStrictEqual(Arr.groupBy((_) => "")([]), {}) deepStrictEqual(Arr.groupBy((a) => `${a}`)([1]), { "1": [1] }) deepStrictEqual( Arr.groupBy((s: string) => `${s.length}`)(["foo", "bar", "foobar"]), { "3": ["foo", "bar"], "6": ["foobar"] } ) deepStrictEqual(Arr.groupBy(["a", "b"], (s) => s === "a" ? symA : s === "b" ? symB : symC), { [symA]: ["a"], [symB]: ["b"] }) deepStrictEqual(Arr.groupBy(["a", "b", "c", "d"], (s) => s === "a" ? symA : s === "b" ? symB : symC), { [symA]: ["a"], [symB]: ["b"], [symC]: ["c", "d"] }) }) it("match", () => { const len: (as: ReadonlyArray) => number = Arr.match({ onEmpty: () => 0, onNonEmpty: (as) => 1 + len(as.slice(1)) }) deepStrictEqual(len([1, 2, 3]), 3) }) it("matchLeft", () => { const len: (as: ReadonlyArray) => number = Arr.matchLeft({ onEmpty: () => 0, onNonEmpty: (_, tail) => 1 + len(tail) }) deepStrictEqual(len([1, 2, 3]), 3) }) it("matchRight", () => { const len: (as: ReadonlyArray) => number = Arr.matchRight({ onEmpty: () => 0, onNonEmpty: (init, _) => 1 + len(init) }) deepStrictEqual(len([1, 2, 3]), 3) }) it("sortBy", () => { interface X { readonly a: string readonly b: number readonly c: boolean } const byName = pipe( Str.Order, Order.mapInput((p: { readonly a: string; readonly b: number }) => p.a) ) const byAge = pipe( Num.Order, Order.mapInput((p: { readonly a: string; readonly b: number }) => p.b) ) const sortByNameByAge = Arr.sortBy(byName, byAge) const xs: Arr.NonEmptyArray = [ { a: "a", b: 1, c: true }, { a: "b", b: 3, c: true }, { a: "c", b: 2, c: true }, { a: "b", b: 2, c: true } ] deepStrictEqual(Arr.sortBy()(xs), xs) deepStrictEqual(sortByNameByAge([]), []) deepStrictEqual(sortByNameByAge(xs), [ { a: "a", b: 1, c: true }, { a: "b", b: 2, c: true }, { a: "b", b: 3, c: true }, { a: "c", b: 2, c: true } ]) deepStrictEqual(Arr.sortBy()(new Set(xs)), xs) deepStrictEqual(sortByNameByAge(new Set([])), []) deepStrictEqual(sortByNameByAge(new Set(xs)), [ { a: "a", b: 1, c: true }, { a: "b", b: 2, c: true }, { a: "b", b: 3, c: true }, { a: "c", b: 2, c: true } ]) const sortByAgeByName = Arr.sortBy(byAge, byName) deepStrictEqual(sortByAgeByName(xs), [ { a: "a", b: 1, c: true }, { a: "b", b: 2, c: true }, { a: "c", b: 2, c: true }, { a: "b", b: 3, c: true } ]) }) it("copy", () => { deepStrictEqual(pipe([], Arr.copy), []) deepStrictEqual(pipe([1, 2, 3], Arr.copy), [1, 2, 3]) }) it("chop", () => { deepStrictEqual(pipe([], Arr.chop((as) => [as[0] * 2, as.slice(1)])), []) deepStrictEqual(pipe([1, 2, 3], Arr.chop((as) => [as[0] * 2, as.slice(1)])), [2, 4, 6]) }) it("pad", () => { deepStrictEqual(pipe([], Arr.pad(0, 0)), []) deepStrictEqual(pipe([1, 2, 3], Arr.pad(0, 0)), []) deepStrictEqual(pipe([1, 2, 3], Arr.pad(2, 0)), [1, 2]) deepStrictEqual(pipe([1, 2, 3], Arr.pad(6, 0)), [1, 2, 3, 0, 0, 0]) deepStrictEqual(pipe([1, 2, 3], Arr.pad(-2, 0)), []) }) describe("chunksOf", () => { it("should split a `ReadonlyArray` into length-n pieces", () => { deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]]) deepStrictEqual(Arr.chunksOf(2)([1, 2, 3, 4, 5, 6]), [ [1, 2], [3, 4], [5, 6] ]) deepStrictEqual(Arr.chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) deepStrictEqual(Arr.chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]]) // out of bounds deepStrictEqual(Arr.chunksOf(0)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) deepStrictEqual(Arr.chunksOf(-1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) const assertSingleChunk = (input: ReadonlyArray, n: number) => { const chunks = Arr.chunksOf(n)(input) deepStrictEqual(chunks.length, 1) deepStrictEqual(chunks[0], input) } // n = length assertSingleChunk([1, 2], 2) // n out of bounds assertSingleChunk([1, 2], 3) }) it("returns an empty array if provided an empty array", () => { const empty: ReadonlyArray = [] deepStrictEqual(Arr.chunksOf(0)(empty), Arr.empty()) deepStrictEqual(Arr.chunksOf(0)(Arr.empty()), Arr.empty()) deepStrictEqual(Arr.chunksOf(1)(empty), Arr.empty()) deepStrictEqual(Arr.chunksOf(1)(Arr.empty()), Arr.empty()) deepStrictEqual(Arr.chunksOf(2)(empty), Arr.empty()) deepStrictEqual(Arr.chunksOf(2)(Arr.empty()), Arr.empty()) }) it("should respect the law: chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys)))", () => { const xs: ReadonlyArray = [] const ys: ReadonlyArray = [1, 2] deepStrictEqual( Arr.chunksOf(2)(xs).concat(Arr.chunksOf(2)(ys)), Arr.chunksOf(2)(xs.concat(ys)) ) fc.assert( fc.property( fc.array(fc.integer()).filter((xs) => xs.length % 2 === 0), // Ensures `xs.length` is even fc.array(fc.integer()), fc.integer({ min: 1, max: 1 }).map((x) => x * 2), // Generates `n` to be even so that it evenly divides `xs` (xs, ys, n) => { const as = Arr.chunksOf(n)(xs).concat(Arr.chunksOf(n)(ys)) const bs = Arr.chunksOf(n)(xs.concat(ys)) deepStrictEqual(as, bs) } ) ) }) }) it("makeBy", () => { deepStrictEqual(Arr.makeBy(5, (n) => n * 2), [0, 2, 4, 6, 8]) deepStrictEqual(Arr.makeBy((n) => n * 2)(5), [0, 2, 4, 6, 8]) deepStrictEqual(Arr.makeBy(2.2, (n) => n * 2), [0, 2]) deepStrictEqual(Arr.makeBy((n) => n * 2)(2.2), [0, 2]) }) it("replicate", () => { deepStrictEqual(Arr.replicate("a", 0), ["a"]) deepStrictEqual(Arr.replicate("a", -1), ["a"]) deepStrictEqual(Arr.replicate("a", 3), ["a", "a", "a"]) deepStrictEqual(Arr.replicate("a", 2.2), ["a", "a"]) }) it("range", () => { deepStrictEqual(Arr.range(0, 0), [0]) deepStrictEqual(Arr.range(0, 1), [0, 1]) deepStrictEqual(Arr.range(1, 5), [1, 2, 3, 4, 5]) deepStrictEqual(Arr.range(10, 15), [10, 11, 12, 13, 14, 15]) deepStrictEqual(Arr.range(-1, 0), [-1, 0]) deepStrictEqual(Arr.range(-5, -1), [-5, -4, -3, -2, -1]) // out of bound deepStrictEqual(Arr.range(2, 1), [2]) deepStrictEqual(Arr.range(-1, -2), [-1]) }) it("unionWith", () => { const two: ReadonlyArray = [1, 2] deepStrictEqual(pipe(two, Arr.unionWith([3, 4], Equivalence.strictEqual())), [1, 2, 3, 4]) deepStrictEqual(pipe(two, Arr.unionWith([2, 3], Equivalence.strictEqual())), [1, 2, 3]) deepStrictEqual(pipe(two, Arr.unionWith([1, 2], Equivalence.strictEqual())), [1, 2]) deepStrictEqual(pipe(two, Arr.unionWith(Arr.empty(), Equivalence.strictEqual())), two) deepStrictEqual(pipe(Arr.empty(), Arr.unionWith(two, Equivalence.strictEqual())), two) deepStrictEqual( pipe(Arr.empty(), Arr.unionWith(Arr.empty(), Equivalence.strictEqual())), Arr.empty() ) }) it("intersectionWith", () => { const intersectionWith = Arr.intersectionWith(Equivalence.strictEqual()) deepStrictEqual(pipe([1, 2], intersectionWith([3, 4])), []) deepStrictEqual(pipe([1, 2], intersectionWith([2, 3])), [2]) deepStrictEqual(pipe([1, 2], intersectionWith([1, 2])), [1, 2]) // iterator input deepStrictEqual(pipe([1, 2], intersectionWith([3, 4][Symbol.iterator]())), []) deepStrictEqual(pipe([1, 2], intersectionWith([2, 3][Symbol.iterator]())), [2]) deepStrictEqual(pipe([1, 2], intersectionWith([1, 2][Symbol.iterator]())), [1, 2]) }) it("differenceWith", () => { const differenceWith = Arr.differenceWith(Equivalence.strictEqual()) deepStrictEqual(pipe([1, 2], differenceWith([3, 4])), [1, 2]) deepStrictEqual(pipe([1, 2], differenceWith([2, 3])), [1]) deepStrictEqual(pipe([1, 2], differenceWith([1, 2])), []) // iterator input deepStrictEqual(pipe([1, 2], differenceWith([3, 4][Symbol.iterator]())), [1, 2]) deepStrictEqual(pipe([1, 2], differenceWith([2, 3][Symbol.iterator]())), [1]) deepStrictEqual(pipe([1, 2], differenceWith([1, 2][Symbol.iterator]())), []) }) it("empty", () => { deepStrictEqual(Arr.empty.length, 0) }) it("every", () => { const isPositive: Predicate.Predicate = (n) => n > 0 deepStrictEqual(Arr.every([1, 2, 3], isPositive), true) deepStrictEqual(Arr.every([1, 2, -3], isPositive), false) }) it("some", () => { const isPositive: Predicate.Predicate = (n) => n > 0 deepStrictEqual(Arr.some([-1, -2, 3], isPositive), true) deepStrictEqual(Arr.some([-1, -2, -3], isPositive), false) }) it("length", () => { deepStrictEqual(Arr.length(Arr.empty()), 0) deepStrictEqual(Arr.length([]), 0) deepStrictEqual(Arr.length(["a"]), 1) }) it("fromOption", () => { deepStrictEqual(Arr.fromOption(Option.some("hello")), ["hello"]) deepStrictEqual(Arr.fromOption(Option.none()), []) }) it("forEach", () => { const log: Array = [] Arr.forEach(["a", "b", "c"], (a, i) => log.push(`${a}-${i}`)) deepStrictEqual(log, ["a-0", "b-1", "c-2"]) }) it("sortWith", () => { type X = { a: string b: number } const arr: ReadonlyArray = [{ a: "a", b: 2 }, { a: "b", b: 1 }] deepStrictEqual(Arr.sortWith(arr, (x) => x.b, Order.Number), [{ a: "b", b: 1 }, { a: "a", b: 2 }]) }) it("countBy", () => { deepStrictEqual(Arr.countBy([1, 2, 3, 4, 5], (n) => n % 2 === 0), 2) deepStrictEqual(pipe([1, 2, 3, 4, 5], Arr.countBy((n) => n % 2 === 0)), 2) }) it("isArray", () => { strictEqual(Arr.isArray([]), true) strictEqual(Arr.isArray([1, 2, 3]), true) strictEqual(Arr.isArray("hello"), false) strictEqual(Arr.isArray(123), false) strictEqual(Arr.isArray(null), false) strictEqual(Arr.isArray(undefined), false) strictEqual(Arr.isArray({}), false) strictEqual(Arr.isArray(new Set([1, 2])), false) }) it("fromRecord", () => { deepStrictEqual(Arr.fromRecord({}), []) const entries = Arr.fromRecord({ a: 1, b: 2 }) deepStrictEqual(entries.length, 2) deepStrictEqual(entries.sort(([a], [b]) => a.localeCompare(b)), [["a", 1], ["b", 2]]) }) it("join", () => { deepStrictEqual(Arr.join([], ","), "") deepStrictEqual(Arr.join(["a"], ","), "a") deepStrictEqual(Arr.join(["a", "b", "c"], ", "), "a, b, c") deepStrictEqual(Arr.join(["a", "b", "c"], ""), "abc") deepStrictEqual(pipe(["x", "y"], Arr.join("-")), "x-y") }) it("mapAccum", () => { deepStrictEqual(Arr.mapAccum([1, 2, 3], 0, (acc, n) => [acc + n, acc + n]), [6, [1, 3, 6]]) deepStrictEqual(Arr.mapAccum([], 10, (acc, n: number) => [acc + n, n]), [10, []]) deepStrictEqual( pipe([1, 2, 3], Arr.mapAccum(0, (acc, n) => [acc + n, n * 2])), [6, [2, 4, 6]] ) deepStrictEqual( Arr.mapAccum(["a", "b", "c"], "", (acc, s, i) => [acc + s, `${s}${i}`]), ["abc", ["a0", "b1", "c2"]] ) }) it("partition", () => { const f: (n: number, i: number) => Result.Result = (n, i) => n > 0 ? Result.succeed(n + i) : Result.fail(`negative: ${n}:${i}`) deepStrictEqual(Arr.partition([], f), [[], []]) deepStrictEqual(Arr.partition([1, -2, 3, -4], f), [["negative: -2:1", "negative: -4:3"], [1, 5]]) deepStrictEqual(pipe([5, 10], Arr.partition(f)), [[], [5, 11]]) deepStrictEqual(pipe([-1, -2], Arr.partition(f)), [["negative: -1:0", "negative: -2:1"], []]) deepStrictEqual(pipe(new Set([1, -2, 3, -4]), Arr.partition(f)), [["negative: -2:1", "negative: -4:3"], [ 1, 5 ]]) deepStrictEqual(pipe([1, -2, 3][Symbol.iterator](), Arr.partition(f)), [["negative: -2:1"], [1, 5]]) }) it("partition with typed pass/fail outputs", () => { const items: Array = [1, "a", 2, "b"] deepStrictEqual( Arr.partition(items, (x) => typeof x === "number" ? Result.succeed(x) : Result.fail(x)), [["a", "b"], [1, 2]] ) }) it("getFailures", () => { deepStrictEqual(Arr.getFailures([]), []) deepStrictEqual( Arr.getFailures([Result.succeed(1), Result.fail("err"), Result.succeed(2)]), ["err"] ) deepStrictEqual( Arr.getFailures([Result.fail("a"), Result.fail("b")]), ["a", "b"] ) deepStrictEqual( Arr.getFailures([Result.succeed(1), Result.succeed(2)]), [] ) }) it("getSuccesses", () => { deepStrictEqual(Arr.getSuccesses([]), []) deepStrictEqual( Arr.getSuccesses([Result.succeed(1), Result.fail("err"), Result.succeed(2)]), [1, 2] ) deepStrictEqual( Arr.getSuccesses([Result.fail("a"), Result.fail("b")]), [] ) deepStrictEqual( Arr.getSuccesses([Result.succeed(1), Result.succeed(2)]), [1, 2] ) }) it("dedupe", () => { deepStrictEqual(Arr.dedupe([]), []) deepStrictEqual(Arr.dedupe([1, 2, 1, 3, 2, 4]), [1, 2, 3, 4]) deepStrictEqual(Arr.dedupe([1, 2, 3]), [1, 2, 3]) deepStrictEqual(Arr.dedupe([1, 1, 1]), [1]) deepStrictEqual(Arr.dedupe(["a", "b", "a"]), ["a", "b"]) }) it("dedupeAdjacent", () => { deepStrictEqual(Arr.dedupeAdjacent([]), []) deepStrictEqual(Arr.dedupeAdjacent([1, 2, 3]), [1, 2, 3]) deepStrictEqual(Arr.dedupeAdjacent([1, 1, 2, 2, 3, 3]), [1, 2, 3]) deepStrictEqual(Arr.dedupeAdjacent([1, 2, 1]), [1, 2, 1]) deepStrictEqual(Arr.dedupeAdjacent([1, 1, 1]), [1]) }) it("group", () => { deepStrictEqual(Arr.group([1, 1, 2, 2, 2, 3, 1]), [[1, 1], [2, 2, 2], [3], [1]]) deepStrictEqual(Arr.group([1]), [[1]]) deepStrictEqual(Arr.group(["a", "a", "b"]), [["a", "a"], ["b"]]) }) it("union", () => { deepStrictEqual(Arr.union([1, 2], [2, 3]), [1, 2, 3]) deepStrictEqual(Arr.union([], [1, 2]), [1, 2]) deepStrictEqual(Arr.union([1, 2], []), [1, 2]) deepStrictEqual(Arr.union([], []), []) deepStrictEqual(Arr.union([1, 2], [1, 2]), [1, 2]) deepStrictEqual(pipe([1, 2], Arr.union([3, 4])), [1, 2, 3, 4]) }) it("intersection", () => { deepStrictEqual(Arr.intersection([1, 2, 3], [3, 4, 1]), [1, 3]) deepStrictEqual(Arr.intersection([1, 2], [3, 4]), []) deepStrictEqual(Arr.intersection([1, 2], [1, 2]), [1, 2]) deepStrictEqual(Arr.intersection([], [1, 2]), []) deepStrictEqual(Arr.intersection([1, 2], []), []) deepStrictEqual(pipe([1, 2, 3], Arr.intersection([2, 3, 4])), [2, 3]) }) it("difference", () => { deepStrictEqual(Arr.difference([1, 2, 3], [2, 3, 4]), [1]) deepStrictEqual(Arr.difference([1, 2], [1, 2]), []) deepStrictEqual(Arr.difference([1, 2], []), [1, 2]) deepStrictEqual(Arr.difference([], [1, 2]), []) deepStrictEqual(pipe([1, 2, 3], Arr.difference([3])), [1, 2]) }) it("cartesianWith", () => { deepStrictEqual( Arr.cartesianWith([1, 2], ["a", "b"], (n, s) => `${n}-${s}`), ["1-a", "1-b", "2-a", "2-b"] ) deepStrictEqual(Arr.cartesianWith([], ["a", "b"], (n: number, s) => `${n}-${s}`), []) deepStrictEqual(Arr.cartesianWith([1, 2], [], (n, s: string) => `${n}-${s}`), []) deepStrictEqual( pipe([1], Arr.cartesianWith(["x"], (n, s) => `${n}${s}`)), ["1x"] ) }) it("cartesian", () => { deepStrictEqual(Arr.cartesian([1, 2], ["a", "b"]), [ [1, "a"], [1, "b"], [2, "a"], [2, "b"] ]) deepStrictEqual(Arr.cartesian([], ["a"]), []) deepStrictEqual(Arr.cartesian([1], []), []) deepStrictEqual(Arr.cartesian([1], ["a"]), [[1, "a"]]) deepStrictEqual(pipe([1, 2], Arr.cartesian(["x"])), [[1, "x"], [2, "x"]]) }) it("makeEquivalence", () => { const eq = Arr.makeEquivalence(Equivalence.strictEqual()) strictEqual(eq([], []), true) strictEqual(eq([1, 2, 3], [1, 2, 3]), true) strictEqual(eq([1, 2], [1, 2, 3]), false) strictEqual(eq([1, 2, 3], [1, 2]), false) strictEqual(eq([1, 2, 3], [1, 3, 2]), false) strictEqual(eq([1], [2]), false) }) it("getReadonlyReducerConcat", () => { const reducer = Arr.getReadonlyReducerConcat() deepStrictEqual(reducer.combine([1, 2], [3, 4]), [1, 2, 3, 4]) deepStrictEqual(reducer.combine([], [1, 2]), [1, 2]) deepStrictEqual(reducer.combine([1, 2], []), [1, 2]) deepStrictEqual(reducer.initialValue, []) }) it("makeReducerConcat", () => { const reducer = Arr.makeReducerConcat() deepStrictEqual(reducer.combine(["a"], ["b"]), ["a", "b"]) deepStrictEqual(reducer.initialValue, []) }) it("allocate", () => { deepStrictEqual(Arr.allocate(0).length, 0) deepStrictEqual(Arr.allocate(3).length, 3) }) describe("every - edge cases", () => { it("returns true for empty array", () => { strictEqual(Arr.every([], (_n: number) => false), true) }) it("with index", () => { strictEqual(Arr.every([0, 1, 2], (n, i) => n === i), true) strictEqual(Arr.every([0, 1, 3], (n, i) => n === i), false) }) }) describe("some - edge cases", () => { it("returns false for empty array", () => { strictEqual(Arr.some([], (_n: number) => true), false) }) it("with index", () => { strictEqual(Arr.some([10, 20, 2], (n, i) => n === i), true) strictEqual(Arr.some([10, 20, 30], (n, i) => n === i), false) }) }) describe("copy - edge cases", () => { it("returns a new reference", () => { const original = [1, 2, 3] const copied = Arr.copy(original) deepStrictEqual(copied, original) strictEqual(copied !== original, true) }) }) describe("unfold - edge cases", () => { it("returns empty when seed produces Option.none", () => { deepStrictEqual(Arr.unfold(0, () => Option.none()), []) }) }) it("bindTo", () => { deepStrictEqual(pipe([1, 2, 3], Arr.bindTo("a")), [{ a: 1 }, { a: 2 }, { a: 3 }]) deepStrictEqual(pipe([], Arr.bindTo("a")), []) deepStrictEqual(Arr.bindTo([1], "x"), [{ x: 1 }]) }) it("Do notation", () => { const _do = Arr.Do deepStrictEqual(_do, Arr.of({})) const doA = Arr.bind(_do, "a", () => ["a"]) deepStrictEqual(doA, Arr.of({ a: "a" })) const doAB = Arr.bind(doA, "b", (x) => ["b", x.a + "b"]) deepStrictEqual(doAB, [ { a: "a", b: "b" }, { a: "a", b: "ab" } ]) const doABC = Arr.let(doAB, "c", (x) => [x.a, x.b, x.a + x.b]) deepStrictEqual(doABC, [ { a: "a", b: "b", c: ["a", "b", "ab"] }, { a: "a", b: "ab", c: ["a", "ab", "aab"] } ]) const doABCD = Arr.bind(doABC, "d", () => Arr.empty()) deepStrictEqual(doABCD, []) const doAB__proto__C = pipe( Arr.let(doAB, "__proto__", (x) => [x.a, x.b, x.a + x.b]), Arr.let("c", (x) => [x.a, x.b, x.a + x.b]) ) deepStrictEqual(doAB__proto__C, [ { a: "a", b: "b", c: ["a", "b", "ab"], ["__proto__"]: ["a", "b", "ab"] }, { a: "a", b: "ab", c: ["a", "ab", "aab"], ["__proto__"]: ["a", "ab", "aab"] } ]) }) })