Skip to content

Commit

Permalink
feat(repeat): add repeat module
Browse files Browse the repository at this point in the history
  • Loading branch information
lambdalisue committed Aug 17, 2024
1 parent 1ecde88 commit 8d20e63
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 0 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,48 @@ const joined = await pipe(
console.log(joined); // 12345
```

### repeat

Returns an finite iterable that repeats through the given iterable.

```ts
import { repeat } from "@core/iterutil/repeat";

const iter = repeat([1, 2, 3], 2);
console.log(Array.from(iter)); // [1, 2, 3, 1, 2, 3]
```

```ts
import { repeat } from "@core/iterutil/async/repeat";

const iter = repeat([1, 2, 3], 2);
console.log(await Array.fromAsync(iter)); // [1, 2, 3, 1, 2, 3]
```

Use `pipe` and `pipe/async` modules for [@core/pipe] package like.

```ts
import { pipe } from "@core/pipe";
import { repeat } from "@core/iterutil/pipe/repeat";

const iter = pipe(
[1, 2, 3],
repeat(2),
);
console.log(Array.from(iter)); // [1, 2, 3, 1, 2, 3]
```

```ts
import { pipe } from "@core/pipe";
import { repeat } from "@core/iterutil/pipe/async/repeat";

const iter = pipe(
[1, 2, 3],
repeat(2),
);
console.log(await Array.fromAsync(iter)); // [1, 2, 3, 1, 2, 3]
```

### some

Returns true if at least one element in the iterable satisfies the provided
Expand Down
1 change: 1 addition & 0 deletions async/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from "./nth.ts";
export * from "./pairwise.ts";
export * from "./partition.ts";
export * from "./reduce.ts";
export * from "./repeat.ts";
export * from "./some.ts";
export * from "./take.ts";
export * from "./take_while.ts";
Expand Down
39 changes: 39 additions & 0 deletions async/repeat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Returns an infinite iterable that repeats through the given iterable.
*
* Use {@linkcode https://jsr.io/@core/iterutil/doc/async/cycle/~/cycle cycle} to cycle the iterable
* Use {@linkcode https://jsr.io/@core/iterutil/doc/repeat/~/repeat repeat} to repeat the iterable synchronously.
*
* @param iterable The iterable to repeat.
* @returns The repeatd iterable.
*
* @example
* ```ts
* import { repeat } from "@core/iterutil/async/repeat";
*
* const iter = repeat([1, 2, 3], 2);
* console.log(await Array.fromAsync(iter)); // [1, 2, 3, 1, 2, 3]
* ```
*/
export function repeat<T>(
iterable: Iterable<T> | AsyncIterable<T>,
n: number,
): AsyncIterable<T> {
if (n < 0 || !Number.isSafeInteger(n)) {
throw new RangeError(
`n must be 0 or positive safe integer, but got ${n}.`,

Check warning on line 24 in async/repeat.ts

View check run for this annotation

Codecov / codecov/patch

async/repeat.ts#L23-L24

Added lines #L23 - L24 were not covered by tests
);
}

Check warning on line 26 in async/repeat.ts

View check run for this annotation

Codecov / codecov/patch

async/repeat.ts#L26

Added line #L26 was not covered by tests
if (n === 0) {
return async function* () {}();
}

Check warning on line 29 in async/repeat.ts

View check run for this annotation

Codecov / codecov/patch

async/repeat.ts#L28-L29

Added lines #L28 - L29 were not covered by tests
return async function* () {
const array = await Array.fromAsync(iterable);
if (array.length === 0) {
return;
}

Check warning on line 34 in async/repeat.ts

View check run for this annotation

Codecov / codecov/patch

async/repeat.ts#L33-L34

Added lines #L33 - L34 were not covered by tests
for (let i = 0; i < n; i++) {
yield* array;
}
}();
}
53 changes: 53 additions & 0 deletions async/repeat_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { assertEquals } from "@std/assert";
import { assertType, type IsExact } from "@std/testing/types";
import { toAsyncIterable } from "./to_async_iterable.ts";
import { take } from "./take.ts";
import { cycle } from "./cycle.ts";

Deno.test("cycle", async (t) => {
await t.step("with async iterable", async (t) => {
await t.step("with non empty iterable", async () => {
const result = cycle(toAsyncIterable([0, 1, 2]));
const expected = [0, 1, 2, 0, 1];
assertEquals(await Array.fromAsync(take(result, 5)), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});

await t.step("with single value iterable", async () => {
const result = cycle(toAsyncIterable([0]));
const expected = [0, 0, 0, 0, 0];
assertEquals(await Array.fromAsync(take(result, 5)), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});

await t.step("with empty iterable", async () => {
const result = cycle(toAsyncIterable([] as number[]));
const expected: number[] = [];
assertEquals(await Array.fromAsync(take(result, 5)), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});
});

await t.step("with iterable", async (t) => {
await t.step("with non empty iterable", async () => {
const result = cycle([0, 1, 2]);
const expected = [0, 1, 2, 0, 1];
assertEquals(await Array.fromAsync(take(result, 5)), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});

await t.step("with single value iterable", async () => {
const result = cycle([0]);
const expected = [0, 0, 0, 0, 0];
assertEquals(await Array.fromAsync(take(result, 5)), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});

await t.step("with empty iterable", async () => {
const result = cycle([] as number[]);
const expected: number[] = [];
assertEquals(await Array.fromAsync(take(result, 5)), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});
});
});
8 changes: 8 additions & 0 deletions deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"./async/pairwise": "./async/pairwise.ts",
"./async/partition": "./async/partition.ts",
"./async/reduce": "./async/reduce.ts",
"./async/repeat": "./async/repeat.ts",
"./async/some": "./async/some.ts",
"./async/take": "./async/take.ts",
"./async/take-while": "./async/take_while.ts",
Expand Down Expand Up @@ -77,6 +78,7 @@
"./pipe/async/pairwise": "./pipe/async/pairwise.ts",
"./pipe/async/partition": "./pipe/async/partition.ts",
"./pipe/async/reduce": "./pipe/async/reduce.ts",
"./pipe/async/repeat": "./pipe/async/repeat.ts",
"./pipe/async/some": "./pipe/async/some.ts",
"./pipe/async/take": "./pipe/async/take.ts",
"./pipe/async/take-while": "./pipe/async/take_while.ts",
Expand All @@ -103,13 +105,15 @@
"./pipe/pairwise": "./pipe/pairwise.ts",
"./pipe/partition": "./pipe/partition.ts",
"./pipe/reduce": "./pipe/reduce.ts",
"./pipe/repeat": "./pipe/repeat.ts",
"./pipe/some": "./pipe/some.ts",
"./pipe/take": "./pipe/take.ts",
"./pipe/take-while": "./pipe/take_while.ts",
"./pipe/uniq": "./pipe/uniq.ts",
"./pipe/zip": "./pipe/zip.ts",
"./range": "./range.ts",
"./reduce": "./reduce.ts",
"./repeat": "./repeat.ts",
"./some": "./some.ts",
"./take": "./take.ts",
"./take-while": "./take_while.ts",
Expand Down Expand Up @@ -156,6 +160,7 @@
"@core/iterutil/async/pairwise": "./async/pairwise.ts",
"@core/iterutil/async/partition": "./async/partition.ts",
"@core/iterutil/async/reduce": "./async/reduce.ts",
"@core/iterutil/async/repeat": "./async/repeat.ts",
"@core/iterutil/async/some": "./async/some.ts",
"@core/iterutil/async/take": "./async/take.ts",
"@core/iterutil/async/take-while": "./async/take_while.ts",
Expand Down Expand Up @@ -209,6 +214,7 @@
"@core/iterutil/pipe/async/pairwise": "./pipe/async/pairwise.ts",
"@core/iterutil/pipe/async/partition": "./pipe/async/partition.ts",
"@core/iterutil/pipe/async/reduce": "./pipe/async/reduce.ts",
"@core/iterutil/pipe/async/repeat": "./pipe/async/repeat.ts",
"@core/iterutil/pipe/async/some": "./pipe/async/some.ts",
"@core/iterutil/pipe/async/take": "./pipe/async/take.ts",
"@core/iterutil/pipe/async/take-while": "./pipe/async/take_while.ts",
Expand Down Expand Up @@ -237,13 +243,15 @@
"@core/iterutil/pipe/pairwise": "./pipe/pairwise.ts",
"@core/iterutil/pipe/partition": "./pipe/partition.ts",
"@core/iterutil/pipe/reduce": "./pipe/reduce.ts",
"@core/iterutil/pipe/repeat": "./pipe/repeat.ts",
"@core/iterutil/pipe/some": "./pipe/some.ts",
"@core/iterutil/pipe/take": "./pipe/take.ts",
"@core/iterutil/pipe/take-while": "./pipe/take_while.ts",
"@core/iterutil/pipe/uniq": "./pipe/uniq.ts",
"@core/iterutil/pipe/zip": "./pipe/zip.ts",
"@core/iterutil/range": "./range.ts",
"@core/iterutil/reduce": "./reduce.ts",
"@core/iterutil/repeat": "./repeat.ts",
"@core/iterutil/some": "./some.ts",
"@core/iterutil/take": "./take.ts",
"@core/iterutil/take-while": "./take_while.ts",
Expand Down
1 change: 1 addition & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from "./pairwise.ts";
export * from "./partition.ts";
export * from "./range.ts";
export * from "./reduce.ts";
export * from "./repeat.ts";
export * from "./some.ts";
export * from "./take.ts";
export * from "./take_while.ts";
Expand Down
1 change: 1 addition & 0 deletions pipe/async/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from "./nth.ts";
export * from "./pairwise.ts";
export * from "./partition.ts";
export * from "./reduce.ts";
export * from "./repeat.ts";
export * from "./some.ts";
export * from "./take.ts";
export * from "./take_while.ts";
Expand Down
24 changes: 24 additions & 0 deletions pipe/async/repeat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { repeat as base } from "@core/iterutil/async/repeat";

/**
* An operator to return a function that repeats the elements of an iterable.
*
* See {@linkcode https://jsr.io/@core/iterutil/doc/async/repeat/~/repeat repeat} for native repeat.
*
* @example
* ```ts
* import { pipe } from "@core/pipe";
* import { repeat } from "@core/iterutil/pipe/async/repeat";
*
* const iter = pipe(
* [1, 2, 3],
* repeat(2),
* );
* console.log(await Array.fromAsync(iter)); // [1, 2, 3, 1, 2, 3]
* ```
*/
export function repeat<T>(
n: number,
): (iterable: Iterable<T> | AsyncIterable<T>) => AsyncIterable<T> {
return (iterable: Iterable<T> | AsyncIterable<T>) => base(iterable, n);
}
13 changes: 13 additions & 0 deletions pipe/async/repeat_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { assertEquals } from "@std/assert";
import { assertType, type IsExact } from "@std/testing/types";
import { pipe } from "@core/pipe";
import { repeat } from "./repeat.ts";

Deno.test("repeat", async (t) => {
await t.step("usage", async () => {
const result = pipe([0, 1, 2], repeat(2));
const expected = [0, 1, 2, 0, 1, 2];
assertEquals(await Array.fromAsync(result), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});
});
1 change: 1 addition & 0 deletions pipe/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from "./nth.ts";
export * from "./pairwise.ts";
export * from "./partition.ts";
export * from "./reduce.ts";
export * from "./repeat.ts";
export * from "./some.ts";
export * from "./take.ts";
export * from "./take_while.ts";
Expand Down
22 changes: 22 additions & 0 deletions pipe/repeat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { repeat as base } from "@core/iterutil/repeat";

/**
* An operator to return a function that repeats the elements of an iterable.
*
* See {@linkcode https://jsr.io/@core/iterutil/doc/repeat/~/repeat repeat} for native repeat.
*
* @example
* ```ts
* import { pipe } from "@core/pipe";
* import { repeat } from "@core/iterutil/pipe/repeat";
*
* const iter = pipe(
* [1, 2, 3],
* repeat(2),
* );
* console.log(Array.from(iter)); // [1, 2, 3, 1, 2, 3]
* ```
*/
export function repeat<T>(n: number): (iterable: Iterable<T>) => Iterable<T> {
return (iterable: Iterable<T>) => base(iterable, n);
}

Check warning on line 22 in pipe/repeat.ts

View check run for this annotation

Codecov / codecov/patch

pipe/repeat.ts#L19-L22

Added lines #L19 - L22 were not covered by tests
14 changes: 14 additions & 0 deletions pipe/repeat_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { assertEquals } from "@std/assert";
import { assertType, type IsExact } from "@std/testing/types";
import { pipe } from "@core/pipe";
import { take } from "./take.ts";
import { cycle } from "./cycle.ts";

Deno.test("cycle", async (t) => {
await t.step("usage", () => {
const result = pipe([0, 1, 2], cycle, take(5));
const expected = [0, 1, 2, 0, 1];
assertEquals(Array.from(result), expected);
assertType<IsExact<typeof result, Iterable<number>>>(true);
});
});
38 changes: 38 additions & 0 deletions repeat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Returns a finite iterable that repeats through the given iterable.
*
* Use {@linkcode https://jsr.io/@core/iterutil/doc/cycle/~/cycle cycle} to cycle the iterable.
* Use {@linkcode https://jsr.io/@core/iterutil/doc/async/repeat/~/repeat repeat} to repeat the iterable asynchronously.
*
* @param iterable The iterable to repeat.
* @param n The number of times to repeat the iterable. Must be a safe integer greater than or equal to 0
* @returns The repeated iterable.
* @throws {RangeError} If `n` is not a safe integer greater than or equal to 0.
*
* @example
* ```ts
* import { repeat } from "@core/iterutil/repeat";
*
* const iter = repeat([1, 2, 3], 2);
* console.log(Array.from(iter)); // [1, 2, 3, 1, 2, 3]
* ```
*/
export function repeat<T>(iterable: Iterable<T>, n: number): Iterable<T> {
if (n < 0 || !Number.isSafeInteger(n)) {
throw new RangeError(
`n must be 0 or positive safe integer, but got ${n}.`,

Check warning on line 23 in repeat.ts

View check run for this annotation

Codecov / codecov/patch

repeat.ts#L22-L23

Added lines #L22 - L23 were not covered by tests
);
}

Check warning on line 25 in repeat.ts

View check run for this annotation

Codecov / codecov/patch

repeat.ts#L25

Added line #L25 was not covered by tests
if (n === 0) {
return [];
}

Check warning on line 28 in repeat.ts

View check run for this annotation

Codecov / codecov/patch

repeat.ts#L27-L28

Added lines #L27 - L28 were not covered by tests
return function* () {
const array = Array.from(iterable);
if (array.length === 0) {
return;
}
for (let i = 0; i < n; i++) {
yield* array;
}
}();
}
26 changes: 26 additions & 0 deletions repeat_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { assertEquals } from "@std/assert";
import { assertType, type IsExact } from "@std/testing/types";
import { repeat } from "./repeat.ts";

Deno.test("repeat", async (t) => {
await t.step("with non empty iterable", () => {
const result = repeat([0, 1, 2], 2);
const expected = [0, 1, 2, 0, 1, 2];
assertEquals(Array.from(result), expected);
assertType<IsExact<typeof result, Iterable<number>>>(true);
});

await t.step("with single value iterable", () => {
const result = repeat([0], 2);
const expected = [0, 0];
assertEquals(Array.from(result), expected);
assertType<IsExact<typeof result, Iterable<number>>>(true);
});

await t.step("with empty iterable", () => {
const result = repeat([] as number[], 2);
const expected: number[] = [];
assertEquals(Array.from(result), expected);
assertType<IsExact<typeof result, Iterable<number>>>(true);
});
});

0 comments on commit 8d20e63

Please sign in to comment.