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

feat(flow): implement flow #595

Merged
merged 3 commits into from
Sep 26, 2024
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
21 changes: 21 additions & 0 deletions benchmarks/performance/flow.bench.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
48 changes: 48 additions & 0 deletions docs/reference/function/flow.md
Original file line number Diff line number Diff line change
@@ -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
```
48 changes: 48 additions & 0 deletions docs/zh_hans/reference/function/flow.md
Original file line number Diff line number Diff line change
@@ -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 changes: 53 additions & 0 deletions src/function/flow.spec.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 155 additions & 0 deletions src/function/flow.ts
Original file line number Diff line number Diff line change
@@ -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;
};
}
1 change: 1 addition & 0 deletions src/function/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Loading