Skip to content

Commit

Permalink
feat(flatfold): implement a concatenating version of fold
Browse files Browse the repository at this point in the history
It's like flatMap, but for folds.

Closes #4
  • Loading branch information
RobertFischer committed Dec 14, 2020
1 parent 86ccf64 commit a2c7cb4
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 3 deletions.
26 changes: 26 additions & 0 deletions src/fun-promise.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -737,4 +737,30 @@ describe("FunPromise", () => {
});
});
});

describe("flatFold", () => {
_.forEach([true, false], (staticVersion) => {
describe(staticVersion ? "static" : "instance", () => {
function doFlatFold(values, initialValues, accumulator) {
if (staticVersion) {
return FunPromise.flatFold(values, initialValues, accumulator);
} else {
return FunPromise.resolve(values).flatFold(
initialValues,
accumulator
);
}
}

it("basically works", async () => {
const values = [4, 5, 6];
const initialValues = [1, 2, 3];
const accumulator = (xs, x) => [x, x];
await expect(
doFlatFold(values, initialValues, accumulator)
).resolves.toEqual([1, 2, 3, 4, 4, 5, 5, 6, 6]);
});
});
});
});
});
44 changes: 41 additions & 3 deletions src/fun-promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,9 @@ export default class FunPromise<T> implements Promise<T> {
resolveValues: boolean = false,
sequentialResolution: boolean = false
): FunPromise<Item<T>[]> {
const aryPromise: FunPromise<Item<T>[]> = this.then((iter) => [
...((iter as unknown) as Iterable<Item<T>>),
]);
const aryPromise = (this.then(
async (iterPromise) => _toArray(await iterPromise) // Just to be sure we're all de-promise'd
) as unknown) as FunPromise<Item<T>[]>;
if (resolveValues) {
if (sequentialResolution) {
return aryPromise.then(async (ary) => {
Expand Down Expand Up @@ -698,6 +698,44 @@ export default class FunPromise<T> implements Promise<T> {
return FunPromise.resolve(values).fold(initialValue, accumulator);
}

/**
* Given an initial array of values and an accumulator function, apply the accumlator function to each element of the promise's resolved value,
* passing in the current array of values and the resolved item. Returns an array with the concatenated results of the accumulation.
* If any of the promise's values are rejected, the entire operation will be rejected.
*
* The resolution order is not guaranteed. The accumulator function will be passed values as those values resolve.
*/
flatFold<T2 = Item<T>>(
initialValue: PromisableIterable<T2>,
accumulator: (memo: T2[], it: Item<T>) => PromisableIterable<T2>
): FunPromise<T2[]> {
return this.arrayify().then(async (ary: Promisable<Item<T>>[]) => {
let memoPromise: FunPromise<T2[]> = FunPromise.resolve(
initialValue
).arrayify() as FunPromise<T2[]>;
await Promise.all(
_map(ary, async (promisableValue) => {
const value = await promisableValue;
memoPromise = memoPromise.then(async (memo) =>
memo.concat(_toArray(await accumulator(memo, value)))
);
})
);
return memoPromise;
});
}

/**
* Equivalent to `FunPromise.resolve(values).flatFold(initialValue, accumulator)`.
*/
static flatFold<T, T2 = T>(
values: PromisableIterable<T>,
initialValue: PromisableIterable<T2>,
accumulator: (memo: T2[], it: T) => PromisableIterable<T2>
): FunPromise<T2[]> {
return FunPromise.resolve(values).flatFold(initialValue, accumulator);
}

/**
* Handles rejections like 'catch', but wraps them in a [[`NestedError`]] with the given message.
*/
Expand Down

0 comments on commit a2c7cb4

Please sign in to comment.