Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AsyncIterable depends on AsyncIterator utils, remove useless reduceAsyncIterator #84

Merged
merged 1 commit into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 14 additions & 42 deletions src/utils/__tests__/async.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { isRight, left, right, Right } from "fp-ts/lib/Either";
import {
filterAsyncIterator,
flattenAsyncIterator,
reduceAsyncIterator
} from "../async";
import { filterAsyncIterator, flattenAsyncIterator } from "../async";

const mockNext = jest.fn();
const mockAsyncIterator = {
Expand Down Expand Up @@ -68,12 +64,13 @@ describe("flattenAsyncIterator utils", () => {
});
});

describe("mapEitherAsyncIterator utils", () => {
describe("filterAsyncIterator utils", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("should filter values that match the predicate", async () => {
const expectedRightValue = right(1);
const expectedReturnValue = true;
mockNext.mockImplementationOnce(async () => ({
done: false,
value: left(new Error("Left value error"))
Expand All @@ -84,7 +81,7 @@ describe("mapEitherAsyncIterator utils", () => {
}));
mockNext.mockImplementationOnce(async () => ({
done: true,
value: undefined
value: expectedReturnValue
}));
const iter = filterAsyncIterator<Right<Error, number>>(
mockAsyncIterator,
Expand All @@ -94,12 +91,16 @@ describe("mapEitherAsyncIterator utils", () => {
done: false,
value: expectedRightValue
});
expect(await iter.next()).toEqual({ done: true, value: undefined });
expect(await iter.next()).toEqual({
done: true,
value: expectedReturnValue
});
expect(mockNext).toBeCalledTimes(3);
});

it("should skip all values if don't match the predicate", async () => {
const leftValue = left(new Error("Left value error"));
const expectedReturnValue = true;
mockNext.mockImplementationOnce(async () => ({
done: false,
value: leftValue
Expand All @@ -110,45 +111,16 @@ describe("mapEitherAsyncIterator utils", () => {
}));
mockNext.mockImplementationOnce(async () => ({
done: true,
value: undefined
value: expectedReturnValue
}));
const iter = filterAsyncIterator<Right<Error, number>>(
mockAsyncIterator,
isRight
);
expect(await iter.next()).toEqual({ done: true, value: undefined });
expect(mockNext).toBeCalledTimes(3);
});
});

describe("reduceAsyncIterator", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("should reduce the documents of the wrapped iterator", async () => {
mockNext.mockImplementationOnce(async () => ({
done: false,
value: ["1", "2"]
}));
mockNext.mockImplementationOnce(async () => ({
done: false,
value: ["3", "4"]
}));
mockNext.mockImplementationOnce(async () => ({
expect(await iter.next()).toEqual({
done: true,
value: undefined
}));

const iter = reduceAsyncIterator(
mockAsyncIterator,
(prev: string, cur: string) => prev + cur,
""
);

const result1 = await iter.next();
expect(result1).toEqual({ done: false, value: "12" });
const result2 = await iter.next();
expect(result2).toEqual({ done: false, value: "34" });
expect(await iter.next()).toEqual({ done: true, value: undefined });
value: expectedReturnValue
});
expect(mockNext).toBeCalledTimes(3);
});
});
127 changes: 61 additions & 66 deletions src/utils/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,70 +50,62 @@ export async function asyncIterableToArray<T>(
}

/**
* Create a new AsyncIterable providing only the values that satisfy the predicate function.
* Create a new AsyncIterator providing only the values that satisfy the predicate function.
* The predicate function is also an optional Type Guard function if types T and K are different.
*
* Example:
* ```
* const i: AsyncIterable<Either<E, A>> = {} as AsyncIterable<Either<E, A>>;
* const f: AsyncIterable<Right<E, A>> = filterAsyncIterable<Either<E, A>, Right<E, A>>(i, isRight);
* const i: AsyncIterator<Either<E, A>> = {} as AsyncIterator<Either<E, A>>;
* const newI: AsyncIterator<Right<E, A>> = filterAsyncIterator<Either<E, A>, Right<E, A>>(i, isRight);
* ```
* @param iterable Original AsyncIterable
* @param iter Original AsyncIterator
* @param predicate Predicate function
*/
export const filterAsyncIterable = <T, K = T>(
iterable: AsyncIterable<T | K>,
export function filterAsyncIterator<T, K = T>(
iter: AsyncIterator<T | K>,
predicate: (value: T | K) => value is K
): AsyncIterable<K> => ({
async *[Symbol.asyncIterator](): AsyncIterator<K> {
// tslint:disable-next-line: await-promise
for await (const value of iterable) {
// tslint:disable-next-line: no-any
): AsyncIterator<K, any> {
// tslint:disable-next-line: no-any
async function* getValues(): AsyncGenerator<K, any, unknown> {
while (true) {
const { done, value } = await iter.next();
if (done) {
return value;
}
if (predicate(value)) {
yield value;
}
}
}
});
return {
next: async () => {
return await getValues().next();
}
};
}

/**
* Create a new AsyncIterator providing only the values that satisfy the predicate function.
* Create a new AsyncIterable providing only the values that satisfy the predicate function.
* The predicate function is also an optional Type Guard function if types T and K are different.
*
* Example:
* ```
* const i: AsyncIterator<Either<E, A>> = {} as AsyncIterator<Either<E, A>>;
* const newI: AsyncIterator<Right<E, A>> = filterAsyncIterator<Either<E, A>, Right<E, A>>(iterable, isRight);
* const i: AsyncIterable<Either<E, A>> = {} as AsyncIterable<Either<E, A>>;
* const f: AsyncIterable<Right<E, A>> = filterAsyncIterable<Either<E, A>, Right<E, A>>(i, isRight);
* ```
* @param iter Original AsyncIterator
* @param iterable Original AsyncIterable
* @param predicate Predicate function
*/
export function filterAsyncIterator<T, K = T>(
iter: AsyncIterator<T | K>,
export const filterAsyncIterable = <T, K = T>(
source: AsyncIterable<T | K>,
predicate: (value: T | K) => value is K
): AsyncIterator<K> {
const iterable = {
[Symbol.asyncIterator]: () => iter
): AsyncIterable<K> => {
const iter = source[Symbol.asyncIterator]();
return {
[Symbol.asyncIterator]: () => filterAsyncIterator(iter, predicate)
};
return filterAsyncIterable(iterable, predicate)[Symbol.asyncIterator]();
}

/**
* Create a new AsyncIterable which provide one by one the values ​​contained into the input AsyncIterable
*
* @param iterable Original AsyncIterable
*/
export const flattenAsyncIterable = <T>(
iterable: AsyncIterable<ReadonlyArray<T>>
): AsyncIterable<T> => ({
async *[Symbol.asyncIterator](): AsyncIterator<T> {
// tslint:disable-next-line: await-promise
for await (const value of iterable) {
for (const item of value) {
yield item;
}
}
}
});
};

/**
* Create a new AsyncIterator which provide one by one the values ​​contained into the input AsyncIterator
Expand All @@ -122,35 +114,38 @@ export const flattenAsyncIterable = <T>(
*/
export function flattenAsyncIterator<T>(
iter: AsyncIterator<ReadonlyArray<T>>
): AsyncIterator<T> {
const iterable = {
[Symbol.asyncIterator]: () => iter
};
return flattenAsyncIterable(iterable)[Symbol.asyncIterator]();
}

export function reduceAsyncIterable<A, B>(
iterable: AsyncIterable<ReadonlyArray<A>>,
reducer: (previousValue: B, currentValue: A) => B,
init: B
): AsyncIterable<B> {
return {
async *[Symbol.asyncIterator](): AsyncIterator<B> {
// tslint:disable-next-line: await-promise
for await (const value of iterable) {
yield value.reduce<B>(reducer, init);
// tslint:disable-next-line: no-any
): AsyncIterator<T, any> {
// tslint:disable-next-line: no-let readonly-array
let array: T[] = [];
// tslint:disable-next-line: no-any
async function* getValues(): AsyncGenerator<T, any, unknown> {
while (array.length === 0) {
const { done, value } = await iter.next();
if (done) {
return value;
}
array = Array.from(value);
}
yield array.shift() as T;
}
return {
next: async () => {
return await getValues().next();
}
};
}

export function reduceAsyncIterator<A, B>(
i: AsyncIterator<ReadonlyArray<A>>,
reducer: (previousValue: B, currentValue: A) => B,
init: B
): AsyncIterator<B> {
const iterable = {
[Symbol.asyncIterator]: () => i
/**
* Create a new AsyncIterable which provide one by one the values ​​contained into the input AsyncIterable
*
* @param source Original AsyncIterable
*/
export const flattenAsyncIterable = <T>(
source: AsyncIterable<ReadonlyArray<T>>
): AsyncIterable<T> => {
const iter = source[Symbol.asyncIterator]();
return {
[Symbol.asyncIterator]: () => flattenAsyncIterator(iter)
};
return reduceAsyncIterable(iterable, reducer, init)[Symbol.asyncIterator]();
}
};