import { describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; import { all, iter, range } from "./builtins.ts"; import { chain, compress, count, cycle, dropwhile, groupby, ifilter, imap, islice, permutations, repeat, takewhile, zipLongest, zipLongest3, zipMany, } from "./itertools.ts"; import { take } from "./more-itertools.ts"; const isEven = (x: number) => x % 2 === 0; const isPositive = (x: number) => x >= 0; describe("chain", () => { it("chains empty iterables", () => { expect(Array.from(chain([], []))).toEqual([]); }); it("chains iterables together", () => { expect(Array.from(chain(["foo"], []))).toEqual(["foo"]); expect(Array.from(chain([], ["bar"]))).toEqual(["bar"]); expect(Array.from(chain([], ["bar"], ["qux"]))).toEqual(["bar", "qux"]); expect(Array.from(chain(["foo", "bar"], ["qux"]))).toEqual([ "foo", "bar", "qux", ]); }); }); describe("compress", () => { it("compress on empty list", () => { expect(compress([], [])).toEqual([]); }); it("compress removes selected items", () => { expect(compress("abc", [])).toEqual([]); expect(compress("abc", [true])).toEqual(["a"]); expect(compress("abc", [false, false, false])).toEqual([]); expect(compress("abc", [true, false, true])).toEqual(["a", "c"]); }); }); describe("count", () => { it("default counter", () => { expect(take(6, count())).toEqual([0, 1, 2, 3, 4, 5]); }); it("counter from different start value", () => { expect(take(6, count(1))).toEqual([1, 2, 3, 4, 5, 6]); expect(take(6, count(-3))).toEqual([-3, -2, -1, 0, 1, 2]); }); it("counter backwards", () => { expect(take(6, count(4, -1))).toEqual([4, 3, 2, 1, 0, -1]); expect(take(5, count(-3, -2))).toEqual([-3, -5, -7, -9, -11]); }); }); describe("cycle", () => { it("cycle with empty list", () => { expect(Array.from(cycle([]))).toEqual([]); }); it("cycles", () => { // We'll have to wrap it in a take() call to avoid infinite-length arrays :) expect(take(3, cycle(["x"]))).toEqual(["x", "x", "x"]); expect(take(5, cycle(["even", "odd"]))).toEqual([ "even", "odd", "even", "odd", "even", ]); }); it("cycles with infinite iterable", () => { // Function `cycle` should properly work with infinite iterators (`repeat('x')` in this case) expect(take(3, cycle(repeat("x")))).toEqual(["x", "x", "x"]); }); }); describe("dropwhile", () => { it("dropwhile on empty list", () => { expect(Array.from(dropwhile([], isEven))).toEqual([]); expect(Array.from(dropwhile([], isPositive))).toEqual([]); }); it("dropwhile on list", () => { expect(Array.from(dropwhile([1], isEven))).toEqual([1]); expect(Array.from(dropwhile([1], isPositive))).toEqual([]); expect(Array.from(dropwhile([-1, 0, 1], isEven))).toEqual([-1, 0, 1]); expect(Array.from(dropwhile([4, -1, 0, 1], isEven))).toEqual([-1, 0, 1]); expect(Array.from(dropwhile([-1, 0, 1], isPositive))).toEqual([-1, 0, 1]); expect(Array.from(dropwhile([7, -1, 0, 1], isPositive))).toEqual([ -1, 0, 1, ]); expect(Array.from(dropwhile([0, 2, 4, 6, 7, 8, 10], isEven))).toEqual([ 7, 8, 10, ]); expect(Array.from(dropwhile([0, 1, 2, -2, 3, 4, 5, 6, 7], isPositive))) .toEqual([-2, 3, 4, 5, 6, 7]); }); }); describe("groupby", () => { // deno-lint-ignore no-explicit-any const countValues = (grouped: Iterable<any>) => Array.from(imap(grouped, ([k, v]) => [k, Array.from(v).length])); it("groupby with empty list", () => { expect(Array.from(groupby([]))).toEqual([]); }); it("groups elements", () => { expect(countValues(groupby("aaabbbbcddddaa"))).toEqual([ ["a", 3], ["b", 4], ["c", 1], ["d", 4], ["a", 2], ]); }); it("groups element with key function", () => { expect(countValues(groupby("aaaAbb"))).toEqual([ ["a", 3], ["A", 1], ["b", 2], ]); expect(countValues(groupby("aaaAbb", (val) => val.toUpperCase()))).toEqual([ ["A", 4], ["B", 2], ]); }); it("handles not using the inner iterator", () => { expect(Array.from(imap(groupby("aaabbbbcddddaa"), ([k]) => k))).toEqual([ "a", "b", "c", "d", "a", ]); }); it("handles using the inner iterator after the iteration has advanced", () => { expect(Array.from(groupby("aaabb")).map(([, v]) => Array.from(v))).toEqual([ [], [], ]); const it = iter(groupby("aaabbccc")); // Flow does not like that I use next on an iterable (it is actually // a generator but the Generator type is awful. // $FlowFixMe[prop-missing] const [, v1] = it.next().value; // $FlowFixMe[prop-missing] const [, v2] = it.next().value; // $FlowFixMe[prop-missing] const [, v3] = it.next().value; expect([...v1]).toEqual([]); expect([...v2]).toEqual([]); expect(v3.next().value).toEqual("c"); Array.from(it); // exhaust the groupby iterator expect([...v3]).toEqual([]); }); }); describe("icompress", () => { it("icompress is tested through compress() tests", () => { // This is okay }); }); describe("ifilter", () => { it("ifilter is tested through filter() tests (see builtins)", () => { // This is okay }); it("ifilter can handle infinite inputs", () => { expect(take(5, ifilter(range(9999), isEven))).toEqual([0, 2, 4, 6, 8]); }); }); describe("imap", () => { it("imap is tested through map() tests (see builtins)", () => { // This is okay }); it("...but imap can handle infinite inputs", () => { expect( take( 3, imap(range(9999), (x) => -x), ), ).toEqual([-0, -1, -2]); }); }); describe("islice", () => { it("islice an empty iterable", () => { expect(Array.from(islice([], 2))).toEqual([]); }); it("islice with arguments", () => { expect(Array.from(islice("ABCDEFG", /*stop*/ 2))).toEqual(["A", "B"]); expect(Array.from(islice("ABCDEFG", 2, 4))).toEqual(["C", "D"]); expect(Array.from(islice("ABCDEFG", /*start*/ 2, /*stop*/ null))).toEqual([ "C", "D", "E", "F", "G", ]); expect( Array.from(islice("ABCDEFG", /*start*/ 0, /*stop*/ null, /*step*/ 2)), ).toEqual(["A", "C", "E", "G"]); expect( Array.from(islice("ABCDEFG", /*start*/ 1, /*stop*/ null, /*step*/ 2)), ).toEqual(["B", "D", "F"]); }); }); describe("izip", () => { it("izip is tested through zip() tests (see builtins)", () => { // This is okay }); }); describe("izip3", () => { it("izip3 is tested through zip3() tests (see builtins)", () => { // This is okay }); }); describe("izipMany", () => { it("izipMany is tested through zipMany() tests", () => { // This is okay }); }); describe("izipLongest", () => { it("izipLongest is tested through zipLongest() tests", () => { // This is okay }); }); describe("permutations", () => { it("permutations of empty list", () => { expect(Array.from(permutations([]))).toEqual([[]]); }); it("permutations of unique values", () => { expect(Array.from(permutations([1, 2]))).toEqual([ [1, 2], [2, 1], ]); expect(Array.from(permutations([1, 2, 3]))).toEqual([ [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1], ]); // Duplicates have no effect on the results expect(Array.from(permutations([2, 2, 3]))).toEqual([ [2, 2, 3], [2, 3, 2], [2, 2, 3], [2, 3, 2], [3, 2, 2], [3, 2, 2], ]); }); it("permutations with r param", () => { // r too big expect(Array.from(permutations([1, 2], 5))).toEqual([]); // prettier-ignore expect(Array.from(permutations(range(4), 2))).toEqual([ [0, 1], [0, 2], [0, 3], [1, 0], [1, 2], [1, 3], [2, 0], [2, 1], [2, 3], [3, 0], [3, 1], [3, 2], ]); }); }); describe("repeat", () => { it("repeat indefinitely", () => { // practically limit it to something (in this case 99) const items1 = take(99, repeat(123)); expect(all(items1, (n) => n === 123)).toEqual(true); const items2 = take(99, repeat("foo")); expect(all(items2, (n) => n === "foo")).toEqual(true); }); it("repeat a fixed number of times", () => { const items = repeat("foo", 100); expect(all(items, (n) => n === "foo")).toEqual(true); }); }); describe("takewhile", () => { it("takewhile on empty list", () => { expect(Array.from(takewhile([], isEven))).toEqual([]); expect(Array.from(takewhile([], isPositive))).toEqual([]); }); it("takewhile on list", () => { expect(Array.from(takewhile([1], isEven))).toEqual([]); expect(Array.from(takewhile([1], isPositive))).toEqual([1]); expect(Array.from(takewhile([-1, 0, 1], isEven))).toEqual([]); expect(Array.from(takewhile([-1, 0, 1], isPositive))).toEqual([]); expect(Array.from(takewhile([0, 2, 4, 6, 7, 8, 10], isEven))).toEqual([ 0, 2, 4, 6, ]); expect(Array.from(takewhile([0, 1, 2, -2, 3, 4, 5, 6, 7], isPositive))) .toEqual([0, 1, 2]); }); }); describe("zipMany", () => { it("zipMany with empty iterable", () => { expect(zipMany([])).toEqual([]); expect(zipMany([], [])).toEqual([]); }); it("zipMany takes any number of (homogenous) iterables", () => { expect(zipMany("abc", "ABC")).toEqual([ ["a", "A"], ["b", "B"], ["c", "C"], ]); expect(zipMany("abc", "ABC", "pqrs", "xyz")).toEqual([ ["a", "A", "p", "x"], ["b", "B", "q", "y"], ["c", "C", "r", "z"], ]); }); }); describe("zipLongest", () => { it("zipLongest with empty iterable", () => { expect(zipLongest([], [])).toEqual([]); }); it("zipLongest with two iterables", () => { expect(zipLongest("abc", "")).toEqual([ ["a", undefined], ["b", undefined], ["c", undefined], ]); expect(zipLongest("x", "abc")).toEqual([ ["x", "a"], [undefined, "b"], [undefined, "c"], ]); expect(zipLongest("x", "abc", /* filler */ 0)).toEqual([ ["x", "a"], [0, "b"], [0, "c"], ]); }); }); describe("zipLongest3", () => { it("zipLongest3 with empty iterable", () => { expect(zipLongest3([], [], [])).toEqual([]); }); it("zipLongest3 with two iterables", () => { expect(zipLongest3("abc", "", [1, 2, 3])).toEqual([ ["a", undefined, 1], ["b", undefined, 2], ["c", undefined, 3], ]); expect(zipLongest3("x", "abc", [1, 2, 3])).toEqual([ ["x", "a", 1], [undefined, "b", 2], [undefined, "c", 3], ]); expect(zipLongest3("x", "abc", [1, 2], /* filler */ 0)).toEqual([ ["x", "a", 1], [0, "b", 2], [0, "c", 0], ]); }); });