feat(flow): implement flow (#595)
Some checks are pending
CI / codecov (push) Waiting to run
Release / release (push) Waiting to run

This commit is contained in:
D-Sketon 2024-09-26 15:35:41 +08:00 committed by GitHub
parent f70e36bd7b
commit c051a3165e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 326 additions and 0 deletions

View File

@ -0,0 +1,21 @@
import { bench, describe } from 'vitest';
import { flow as flowToolkit } from 'es-toolkit';
import { flow as flowLodash } from 'lodash';
const flow = flowToolkit;
const flowLo = flowLodash;
describe('flow', () => {
const add = (x: number, y: number) => x + y;
const square = (n: number) => n * n;
bench('es-toolkit/flow', () => {
const combined = flow(add, square);
combined(1, 2);
});
bench('lodash/flow', () => {
const combined = flowLo(add, square);
combined(1, 2);
});
});

View File

@ -0,0 +1,48 @@
# flow
Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
## Signature
```typescript
function flow<R>(f: () => R): () => R;
function flow<A extends any[], R>(f1: (...args: A) => R): (...args: A) => R;
function flow<A extends any[], R1, R2>(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2;
function flow<A extends any[], R1, R2, R3>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3
): (...args: A) => R3;
function flow<A extends any[], R1, R2, R3, R4>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3,
f4: (a: R3) => R4
): (...args: A) => R4;
function flow<A extends any[], R1, R2, R3, R4, R5>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3,
f4: (a: R3) => R4,
f5: (a: R4) => R5
): (...args: A) => R5;
function flow(...funcs: Array<(...args: any[]) => any>): (...args: any[]) => any;
```
### Parameters
- `funcs` (`Array<(...args: any[]) => any>`): The functions to invoke.
### Returns
(`(...args: any[]) => any`): The new composite function.
## Examples
```typescript
const add = (x: number, y: number) => x + y;
const square = (n: number) => n * n;
const combined = flow(add, square);
console.log(combined(1, 2)); // => 9
```

View File

@ -0,0 +1,48 @@
# flow
创建一个函数,该函数返回调用给定函数的结果,并将创建函数的 `this` 绑定传递给这些函数,每次调用时将上一次调用的返回值作为参数传递给下一次调用。
## 签名
```typescript
function flow<R>(f: () => R): () => R;
function flow<A extends any[], R>(f1: (...args: A) => R): (...args: A) => R;
function flow<A extends any[], R1, R2>(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2;
function flow<A extends any[], R1, R2, R3>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3
): (...args: A) => R3;
function flow<A extends any[], R1, R2, R3, R4>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3,
f4: (a: R3) => R4
): (...args: A) => R4;
function flow<A extends any[], R1, R2, R3, R4, R5>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3,
f4: (a: R3) => R4,
f5: (a: R4) => R5
): (...args: A) => R5;
function flow(...funcs: Array<(...args: any[]) => any>): (...args: any[]) => any;
```
### 参数
- `funcs` (`Array<(...args: any[]) => any>`): 需要调用的函数。
### 返回值
(`(...args: any[]) => any`): 新的组合函数。
## 示例
```typescript
const add = (x: number, y: number) => x + y;
const square = (n: number) => n * n;
const combined = flow(add, square);
console.log(combined(1, 2)); // => 9
```

53
src/function/flow.spec.ts Normal file
View File

@ -0,0 +1,53 @@
import { describe, it, expect } from 'vitest';
import { flow } from './flow';
import { curry } from '../compat/function/curry';
import { head } from '../array';
import { ary } from './ary';
import { uniq } from '../array';
const add = function (x: number, y: number) {
return x + y;
};
const square = function (n: number) {
return n * n;
};
describe('flow', () => {
it(`\`flow\` should supply each function with the return value of the previous`, () => {
const fixed = function (n: number) {
return n.toFixed(1);
};
const combined = flow(add, square, fixed);
expect(combined(1, 2)).toBe('9.0');
});
it(`\`flow\` should return a new function`, () => {
const noop = () => {};
const combined = flow(noop);
expect(combined).not.toBe(noop);
});
it(`\`flow\` should work with a curried function and \`_.head\``, () => {
const curried = curry((i: any) => i);
const combined = flow(head, curried);
expect(combined([1])).toBe(1);
});
it(`\`flow\` should work with curried functions with placeholders`, () => {
// TODO change to es-toolkit/compat - map
const map = (array: any[], func: (...args: any[]) => any) => {
return array.map(func);
};
const curried = curry(ary(map, 2), 2);
const getProp = curried(curried.placeholder, (value: { a: any }) => value.a);
const objects = [{ a: 1 }, { a: 2 }, { a: 1 }];
const combined = flow(getProp, uniq);
expect(combined(objects)).toEqual([1, 2]);
});
});

155
src/function/flow.ts Normal file
View File

@ -0,0 +1,155 @@
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {() => R} f The function to invoke.
* @returns {() => R} Returns the new composite function.
*
* @example
* function noArgFunc() {
* return 42;
* }
*
* const combined = flow(noArgFunc);
* console.log(combined()); // 42
*/
export function flow<R>(f: () => R): () => R;
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {(...args: A) => R} f1 The function to invoke.
* @returns {(...args: A) => R} Returns the new composite function.
*
* @example
* function oneArgFunc(a: number) {
* return a * 2;
* }
*
* const combined = flow(oneArgFunc);
* console.log(combined(5)); // 10
*/
export function flow<A extends any[], R>(f1: (...args: A) => R): (...args: A) => R;
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {(...args: A) => R1} f1 The function to invoke.
* @param {(a: R1) => R2} f2 The function to invoke.
* @returns {(...args: A) => R2} Returns the new composite function.
*
* @example
* const add = (x: number, y: number) => x + y;
* const square = (n: number) => n * n;
*
* const combined = flow(add, square);
* console.log(combined(1, 2)); // 9
*/
export function flow<A extends any[], R1, R2>(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2;
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {(...args: A) => R1} f1 The function to invoke.
* @param {(a: R1) => R2} f2 The function to invoke.
* @param {(a: R2) => R3} f3 The function to invoke.
* @returns {(...args: A) => R3} Returns the new composite function.
*
* @example
* const add = (x: number, y: number) => x + y;
* const square = (n: number) => n * n;
* const double = (n: number) => n * 2;
*
* const combined = flow(add, square, double);
* console.log(combined(1, 2)); // 18
*/
export function flow<A extends any[], R1, R2, R3>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3
): (...args: A) => R3;
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {(...args: A) => R1} f1 The function to invoke.
* @param {(a: R1) => R2} f2 The function to invoke.
* @param {(a: R2) => R3} f3 The function to invoke.
* @param {(a: R3) => R4} f4 The function to invoke.
* @returns {(...args: A) => R4} Returns the new composite function.
*
* @example
* const add = (x: number, y: number) => x + y;
* const square = (n: number) => n * n;
* const double = (n: number) => n * 2;
* const toStr = (n: number) => n.toString();
*
* const combined = flow(add, square, double, toStr);
* console.log(combined(1, 2)); // '18'
*/
export function flow<A extends any[], R1, R2, R3, R4>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3,
f4: (a: R3) => R4
): (...args: A) => R4;
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {(...args: A) => R1} f1 The function to invoke.
* @param {(a: R1) => R2} f2 The function to invoke.
* @param {(a: R2) => R3} f3 The function to invoke.
* @param {(a: R3) => R4} f4 The function to invoke.
* @param {(a: R4) => R5} f5 The function to invoke.
* @returns {(...args: A) => R5} Returns the new composite function.
*
* @example
* const add = (x: number, y: number) => x + y;
* const square = (n: number) => n * n;
* const double = (n: number) => n * 2;
* const toStr = (n: number) => n.toString();
* const split = (s: string) => s.split('');
*
* const combined = flow(add, square, double, toStr, split);
* console.log(combined(1, 2)); // ['1', '8']
*/
export function flow<A extends any[], R1, R2, R3, R4, R5>(
f1: (...args: A) => R1,
f2: (a: R1) => R2,
f3: (a: R2) => R3,
f4: (a: R3) => R4,
f5: (a: R4) => R5
): (...args: A) => R5;
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {Array<(...args: any[]) => any>} funcs The functions to invoke.
* @returns {(...args: any[]) => any} Returns the new composite function.
*
* @example
* const add = (x: number, y: number) => x + y;
* const square = (n: number) => n * n;
*
* const combined = flow(add, square);
* console.log(combined(1, 2)); // 9
*/
export function flow(...funcs: Array<(...args: any[]) => any>): (...args: any[]) => any;
/**
* Creates a function that returns the result of invoking the given functions with the `this` binding of the created function, where each successive invocation is supplied the return value of the previous.
*
* @param {Array<(...args: any[]) => any>} funcs The functions to invoke.
* @returns {(...args: any[]) => any} Returns the new composite function.
*
* @example
* const add = (x: number, y: number) => x + y;
* const square = (n: number) => n * n;
*
* const combined = flow(add, square);
* console.log(combined(1, 2)); // 9
*/
export function flow(...funcs: Array<(...args: any[]) => any>): (...args: any[]) => any {
return function (this: any, ...args: any[]) {
let result = funcs.length ? funcs[0].apply(this, args) : args[0];
for (let i = 1; i < funcs.length; i++) {
result = funcs[i].call(this, result);
}
return result;
};
}

View File

@ -13,3 +13,4 @@ export { partialRight } from './partialRight.ts';
export { rest } from './rest.ts';
export { curry } from './curry.ts';
export { spread } from './spread.ts';
export { flow } from './flow.ts';