Skip to content

Commit

Permalink
feat(itertools): implement some other method
Browse files Browse the repository at this point in the history
  • Loading branch information
Baek2back committed Jul 15, 2024
1 parent 858ad3e commit d0fabb1
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 4 deletions.
4 changes: 3 additions & 1 deletion packages/itertools/src/accumulate.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { iter } from "./iter";

type Reducer<T, U> = (acc: U, item: T) => U;

/**
Expand All @@ -14,7 +16,7 @@ export function* accumulate<T, U = T>(
func: Reducer<T, U>,
initial?: U,
): IterableIterator<U> {
const iterator = iterable[Symbol.iterator]();
const iterator = iter(iterable);
let accumulator: U;

if (initial === undefined) {
Expand Down
4 changes: 3 additions & 1 deletion packages/itertools/src/batched.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { iter } from "./iter";

/**
* @example
* ```typescript
Expand All @@ -15,7 +17,7 @@ export function* batched<T>(
throw new Error("n must be at least one");
}

const iterator = iterable[Symbol.iterator]();
const iterator = iter(iterable);

while (true) {
const batch: T[] = [];
Expand Down
35 changes: 35 additions & 0 deletions packages/itertools/src/dropwhile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import { dropwhile } from "./dropwhile";
import { take } from "./take";

describe("dropwhile", () => {
it("dropwhile on empty list", () => {
expect(
take(
Number.POSITIVE_INFINITY,
dropwhile((x) => x < 2, []),
),
).toEqual([]);
});

it("dropwhile on list", () => {
expect(
take(
Number.POSITIVE_INFINITY,
dropwhile((x) => x % 2 === 0, [1]),
),
).toEqual([1]);
expect(
take(
Number.POSITIVE_INFINITY,
dropwhile((x) => x % 2 === 1, [1]),
),
).toEqual([]);
expect(
take(
Number.POSITIVE_INFINITY,
dropwhile((x) => x < 5, [1, 4, 6, 3, 8]),
),
).toEqual([6, 3, 8]);
});
});
22 changes: 22 additions & 0 deletions packages/itertools/src/dropwhile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { iter } from "./iter";

/**
* @example dropwhile((x) => x < 5, [1,4,6,3,8]) // => 6 3 8
*/
export function* dropwhile<T>(
predicate: (item: T) => boolean,
iterable: Iterable<T>,
) {
const it = iter(iterable);
let item = it.next();

while (!item.done) {
if (!predicate(item.value)) break;
item = it.next();
}

while (!item.done) {
yield item.value;
item = it.next();
}
}
23 changes: 23 additions & 0 deletions packages/itertools/src/filterfalse.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { filterfalse } from "./filterfalse";
import { take } from "./take";

describe("filterfalse", () => {
it("should filter elements predicate return true", () => {
expect(
take(
Number.POSITIVE_INFINITY,
filterfalse([1, 4, 6, 3, 8], (x) => x < 5),
),
).toEqual([6, 8]);
});

it("should filter truthy value if predicate did not specify", () => {
expect(
take(
Number.POSITIVE_INFINITY,
filterfalse([1, undefined, 2, null, 3, "", 4, 0, 5]),
),
).toEqual([undefined, null, "", 0]);
});
});
22 changes: 22 additions & 0 deletions packages/itertools/src/filterfalse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @example
* filterfalse((x) => x < 5, [1, 4, 6, 3, 8]) // => 6 8
*/
export function filterfalse<T, U extends T>(
iterable: Iterable<T>,
predicate?: (item: T) => item is U,
): IterableIterator<U>;
export function filterfalse<T>(
iterable: Iterable<T>,
predicate?: (item: T) => boolean,
): IterableIterator<T>;
export function* filterfalse<T>(
iterable: Iterable<T>,
predicate?: (item: T) => boolean,
): IterableIterator<T> {
for (const it of iterable) {
if (!(predicate ?? Boolean)(it)) {
yield it;
}
}
}
22 changes: 22 additions & 0 deletions packages/itertools/src/islice.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, expect, it } from "vitest";
import { islice } from "./islice";

describe("islice", () => {
it("islice iwth empty iterable", () => {
expect([...islice([], 2)]).toEqual([]);
});

it("islice with arguments", () => {
expect([...islice("ABCDEFG", 2)]).toEqual(["A", "B"]);
expect([...islice("ABCDEFG", 2, 4)]).toEqual(["C", "D"]);
expect([...islice("ABCDEFG", 2, null)]).toEqual(["C", "D", "E", "F", "G"]);
expect([...islice("ABCDEFG", 0, null, 2)]).toEqual(["A", "C", "E", "G"]);
});

it("islice invalid arguments", () => {
expect(() => [...islice("ABCDEFG", -2)]).toThrowError();
expect(() => [...islice("ABCDEFG", -2, -3)]).toThrowError();
expect(() => [...islice("ABCDEFG", 0, 3, 0)]).toThrowError();
expect(() => [...islice("ABCDEFG", 0, 3, -1)]).toThrowError();
});
});
55 changes: 55 additions & 0 deletions packages/itertools/src/islice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { iter } from "./iter";

/**
* @example
* islice("ABCDEFG", 2) // => A B
* islice("ABCDEFG", 2, 4) // => C D
* islice("ABCDEFG", 2, null) // => C D E F G
* islice("ABCDEFG", 0, null, 2) // => A C E G
*/
export function islice<T>(
iterable: Iterable<T>,
stop: number,
): IterableIterator<T>;
export function islice<T>(
iterable: Iterable<T>,
start: number,
stop?: number | null,
step?: number,
): IterableIterator<T>;
export function* islice<T>(
iterable: Iterable<T>,
stopOrStart: number,
possiblyStop?: number | null,
step = 1,
): IterableIterator<T> {
let start: number;
let stop: number | null;

if (possiblyStop !== undefined) {
[start, stop] = [stopOrStart, possiblyStop];
} else {
start = 0;
stop = stopOrStart;
}

if (start < 0) throw new Error("start must be positive");
if (stop !== null && stop < 0) throw new Error("stop must be positive");
if (step <= 0) throw new Error("step must be positive");

let index = -1;
const iterator = iter(iterable);
let item: IteratorResult<T>;
while (true) {
index++;
if (stop !== null && index >= stop) return;

item = iterator.next();
if (item.done) return;

if (index < start) continue;
if ((index - start) % step === 0) {
yield item.value;
}
}
}
18 changes: 18 additions & 0 deletions packages/itertools/src/iter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it } from "vitest";
import { iter } from "./iter";

describe("iter", () => {
it("throw TypeError when object is not an Iterable or Iterator", () => {
expect(() => iter(1 as unknown as Iterable<number>)).toThrow(TypeError);
});

it("return iterator when pass iterator itself", () => {
const iterator = [][Symbol.iterator]();
expect(iter(iterator)).toBe(iterator);
});

it("return iterator when pass iterable", () => {
const iterable = "";
expect(iter(iterable)).toStrictEqual(iterable[Symbol.iterator]());
});
});
28 changes: 28 additions & 0 deletions packages/itertools/src/iter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
function isIterable<T = unknown>(
object: Iterable<T> | Iterator<T>,
): object is Iterable<T> {
return (
typeof (object as { [Symbol.iterator]?: unknown })?.[Symbol.iterator] ===
"function"
);
}

function isIterator<T = unknown>(
object: Iterable<T> | Iterator<T>,
): object is Iterator<T> {
return typeof (object as { next?: unknown })?.next === "function";
}

export function iter<T>(
iterableOrIterator: Iterable<T> | Iterator<T>,
): Iterator<T> {
if (isIterator(iterableOrIterator)) {
return iterableOrIterator;
}
if (isIterable(iterableOrIterator)) {
return iterableOrIterator[Symbol.iterator]();
}
throw new TypeError(
"iter: value `iterableOrIterator` must be type of Iterable | Iterator",
);
}
4 changes: 3 additions & 1 deletion packages/itertools/src/take.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { iter } from "./iter";

/**
* Return first `n` items of the iterable as a list.
*
Expand All @@ -22,7 +24,7 @@ export function* itake<T>(
n: number,
iterable: Iterable<T>,
): IterableIterator<T> {
const iterator = iterable[Symbol.iterator]();
const iterator = iter(iterable);

let count = n;
while (count-- > 0) {
Expand Down
4 changes: 3 additions & 1 deletion packages/itertools/src/zip.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { iter } from "./iter";

/**
*
* @example
Expand All @@ -11,7 +13,7 @@ export function* zip<T1, T2>(
): IterableIterator<[T1, T2]> {
const { strict = false } = options;

const [ixs, iys] = [xs[Symbol.iterator](), ys[Symbol.iterator]()];
const [ixs, iys] = [iter(xs), iter(ys)];

while (true) {
const [x, y] = [ixs.next(), iys.next()];
Expand Down

0 comments on commit d0fabb1

Please sign in to comment.