Skip to content

Commit

Permalink
Add scan/asyncScan functions
Browse files Browse the repository at this point in the history
  • Loading branch information
jdesrosiers committed Jun 21, 2023
1 parent f4f49e4 commit a57e9fc
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ console.log(result);
* **asyncFilter**: (fn: Function, iterator: AsyncIterable) => AsyncGenerator

Same as `filter`, but works with promises.
* **scan**: (fn: Function, acc: any, iter: Iterable) => any

Same as `reduce` except it emits the accumulated value after each update
* **asyncScan**: (fn: AsyncReducer, acc: any, iter: AsyncIterable) => Promise<any>

Same as `scan`, but works with promises.
* **flatten**: (iterator: NestedIterable, depth: number = 1) => Generator

Yields values from the iterator with all sub-iterator elements concatenated
Expand Down
51 changes: 51 additions & 0 deletions lib/generators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { expect } from "chai";
import {
map, asyncMap,
filter, asyncFilter,
scan, asyncScan,
flatten, asyncFlatten,
drop, asyncDrop,
take, asyncTake,
Expand Down Expand Up @@ -107,6 +108,56 @@ describe("asyncFilter", () => {
});
});

describe("scan", () => {
let subject: Iterable<number>;

beforeEach(() => {
subject = (function* () {
yield 1;
yield 2;
yield 3;
}());
});

it("uncurried", () => {
const result = scan((acc, n) => acc + n, 0, subject);
expect([...result]).to.eql([1, 3, 6]);
});

it("curried", () => {
const sum = scan((acc, n: number) => acc + n, 0);
const result = sum(subject);
expect([...result]).to.eql([1, 3, 6]);
});
});

describe("asyncScan", () => {
let subject: AsyncGenerator<number>;

beforeEach(() => {
subject = (async function* () {
yield 1;
yield 2;
yield 3;
}());
});

it("uncurried", async () => {
const result = asyncScan((acc, n) => acc + n, 0, subject);
expect((await result.next()).value).to.equal(1);
expect((await result.next()).value).to.equal(3);
expect((await result.next()).value).to.equal(6);
});

it("curried", async () => {
const sum = asyncScan((acc, n: number) => acc + n, 0);
const result = sum(subject);
expect((await result.next()).value).to.equal(1);
expect((await result.next()).value).to.equal(3);
expect((await result.next()).value).to.equal(6);
});
});

describe("flatten", () => {
let subject: NestedIterable<number | string | boolean | null>;
let deeplyNested: NestedIterable<number | string | boolean | null>;
Expand Down
12 changes: 12 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ export const asyncFilter: (
);
export type AsyncPredicate<A> = (item: A) => Promise<boolean> | boolean;

export const scan: (
<A, B>(fn: Reducer<A, B>, acc: B, iter: Iterable<A>) => Generator<B>
) & (
<A, B>(fn: Reducer<A, B>, acc: B) => (iter: Iterable<A>) => Generator<B>
);

export const asyncScan: (
<A, B>(fn: AsyncReducer<A, B>, acc: B, iter: AsyncIterable<A>) => AsyncGenerator<B>
) & (
<A, B>(fn: AsyncReducer<A, B>, acc: B) => (iter: AsyncIterable<A>) => AsyncGenerator<B>
);

export const flatten: <A>(iterator: NestedIterable<A>, depth?: number) => Generator<A | NestedIterable<A>>;
export type NestedIterable<A> = Iterable<A | NestedIterable<A>>;

Expand Down
14 changes: 14 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ export const asyncFilter = curry(async function* (fn, iter) {
}
});

export const scan = curry(function* (fn, acc, iter) {
for (const item of iter) {
acc = fn(acc, item);
yield acc;
}
});

export const asyncScan = curry(async function* (fn, acc, iter) {
for await (const item of iter) {
acc = await fn(acc, item);
yield acc;
}
});

export const flatten = function* (iter, depth = 1) {
for (const n of iter) {
if (depth > 0 && n !== null && typeof n !== "string" && n[Symbol.iterator]) {
Expand Down

0 comments on commit a57e9fc

Please sign in to comment.