Skip to content

Commit

Permalink
Merge pull request #19 from jsr-core/add-repeat
Browse files Browse the repository at this point in the history
feat(repeat): add `repeat` module
  • Loading branch information
lambdalisue authored Aug 17, 2024
2 parents 1ecde88 + fe2b91f commit 2707e3e
Show file tree
Hide file tree
Showing 14 changed files with 315 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}.`,
);
}
if (n === 0) {
return async function* () {}();
}
return async function* () {
const array = await Array.fromAsync(iterable);
if (array.length === 0) {
return;
}
for (let i = 0; i < n; i++) {
yield* array;
}
}();
}
69 changes: 69 additions & 0 deletions async/repeat_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { assertEquals, assertThrows } from "@std/assert";
import { assertType, type IsExact } from "@std/testing/types";
import { toAsyncIterable } from "./to_async_iterable.ts";
import { repeat } from "./repeat.ts";

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

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

await t.step("with empty iterable", async () => {
const result = repeat(toAsyncIterable([] as number[]), 2);
const expected: number[] = [];
assertEquals(await Array.fromAsync(result), 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 = repeat([0, 1, 2], 2);
const expected = [0, 1, 2, 0, 1, 2];
assertEquals(await Array.fromAsync(result), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});

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

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

await t.step("with n=0", async () => {
const result = repeat([0, 1, 2], 0);
const expected: number[] = [];
assertEquals(await Array.fromAsync(result), expected);
assertType<IsExact<typeof result, AsyncIterable<number>>>(true);
});

await t.step("throws RangeError", async (t) => {
await t.step("if the limit is not 0 nor positive safe integer", () => {
assertThrows(() => repeat([], NaN), RangeError);
assertThrows(() => repeat([], Infinity), RangeError);
assertThrows(() => repeat([], -Infinity), RangeError);
assertThrows(() => repeat([], -1), RangeError);
assertThrows(() => repeat([], 1.1), RangeError);
});
});
});
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);
}
13 changes: 13 additions & 0 deletions pipe/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", () => {
const result = pipe([0, 1, 2], repeat(2));
const expected = [0, 1, 2, 0, 1, 2];
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}.`,
);
}
if (n === 0) {
return [];
}
return function* () {
const array = Array.from(iterable);
if (array.length === 0) {
return;
}
for (let i = 0; i < n; i++) {
yield* array;
}
}();
}
Loading

0 comments on commit 2707e3e

Please sign in to comment.