feat(rearg): Implement rearg (#426)

* feat(rearg): implement rearg

* make lint happy

* fix

* make lint happy

---------

Co-authored-by: Sojin Park <raon0211@toss.im>
This commit is contained in:
D-Sketon 2024-09-08 22:40:51 +08:00 committed by GitHub
parent 887aa4d4f4
commit ff595628cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 183 additions and 0 deletions

View File

@ -0,0 +1,18 @@
import { bench, describe } from 'vitest';
import { rearg as reargToolkit } from 'es-toolkit/compat';
import { rearg as reargLodash } from 'lodash';
describe('rearg', () => {
function fn() {
// eslint-disable-next-line prefer-rest-params
return Array.from(arguments);
}
bench('es-toolkit/rearg', () => {
reargToolkit(fn, [2, 0, 1]);
});
bench('lodash/rearg', () => {
reargLodash(fn, [2, 0, 1]);
});
});

View File

@ -0,0 +1,37 @@
# rearg
Creates a function that invokes `func` with arguments arranged according to the specified `indexes` where the argument value at the first index is provided as the first argument, the argument value at the second index is provided as the second argument, and so on.
## Signature
```typescript
function rearg<F extends (...args: any[]) => any>(
func: F,
...indexes: Array<number | number[]>
): (...args: any[]) => ReturnType<F>;
```
### Parameters
- `func` (`F`): The function to rearrange arguments for.
- `indexes` (`Array<number | number[]>`): The arranged argument indexes.
### Returns
(`(...args: any[]) => ReturnType<F>`): Returns the new function.
## Examples
```typescript
import { rearg } from 'es-toolkit/function';
const rearged = rearg(
function (a, b, c) {
return [a, b, c];
},
[2, 0, 1]
);
rearged('b', 'c', 'a');
// => ['a', 'b', 'c']
```

View File

@ -0,0 +1,37 @@
# rearg
创建一个函数,该函数根据指定的 `indexes` 重新排列参数来调用 `func`,其中第一个索引位置的参数值作为第一个参数,第二个索引位置的参数值作为第二个参数,依此类推。
## 签名
```typescript
function rearg<F extends (...args: any[]) => any>(
func: F,
...indexes: Array<number | number[]>
): (...args: any[]) => ReturnType<F>;
```
### 参数
- `func` (`F`): 用于重新排列参数的函数。
- `indexes` (`Array<number | number[]>`): 排列后的参数索引。
### 返回值
(`(...args: any[]) => ReturnType<F>`): 返回新的函数。
## 示例
```typescript
import { rearg } from 'es-toolkit/function';
const rearged = rearg(
function (a, b, c) {
return [a, b, c];
},
[2, 0, 1]
);
rearged('b', 'c', 'a');
// => ['a', 'b', 'c']
```

View File

@ -11,4 +11,5 @@ export { unary } from './unary.ts';
export { partial } from './partial.ts';
export { partialRight } from './partialRight.ts';
export { rest } from './rest.ts';
export { rearg } from './rearg.ts';
export { spread } from './spread.ts';

View File

@ -0,0 +1,62 @@
import { describe, it, expect } from 'vitest';
import { rearg } from './rearg';
describe('rearg', () => {
function fn() {
// eslint-disable-next-line prefer-rest-params
return Array.from(arguments);
}
it('should reorder arguments provided to `func`', () => {
const rearged = rearg(fn, [2, 0, 1]);
expect(rearged('b', 'c', 'a')).toEqual(['a', 'b', 'c']);
});
it('should work with repeated indexes', () => {
const rearged = rearg(fn, [1, 1, 1]);
expect(rearged('c', 'a', 'b')).toEqual(['a', 'a', 'a']);
});
it('should use `undefined` for nonexistent indexes', () => {
const rearged = rearg(fn, [1, 4]);
expect(rearged('b', 'a', 'c')).toEqual(['a', undefined, 'c']);
});
it('should use `undefined` for non-index values', () => {
const values = [{}, null, undefined, false, NaN, '', -1, 1.1];
for (const value of values) {
// @ts-expect-error - invalid args
const rearged = rearg(fn, value);
expect(rearged('a', 'b', 'c')).toEqual([undefined, 'b', 'c']);
}
});
it('should not rearrange arguments when no indexes are given', () => {
let rearged = rearg(fn);
expect(rearged('a', 'b', 'c')).toEqual(['a', 'b', 'c']);
rearged = rearg(fn, [], []);
expect(rearged('a', 'b', 'c')).toEqual(['a', 'b', 'c']);
});
it('should accept multiple index arguments', () => {
const rearged = rearg(fn, 2, 0, 1);
expect(rearged('b', 'c', 'a')).toEqual(['a', 'b', 'c']);
});
it('should accept multiple arrays of indexes', () => {
const rearged = rearg(fn, [2], [0, 1]);
expect(rearged('b', 'c', 'a')).toEqual(['a', 'b', 'c']);
});
it('should work with fewer indexes than arguments', () => {
const rearged = rearg(fn, [1, 0]);
expect(rearged('b', 'a', 'c')).toEqual(['a', 'b', 'c']);
});
it('should work on functions that have been rearged', () => {
const rearged1 = rearg(fn, 2, 1, 0),
rearged2 = rearg(rearged1, 1, 0, 2);
expect(rearged2('b', 'c', 'a')).toEqual(['a', 'b', 'c']);
});
});

28
src/function/rearg.ts Normal file
View File

@ -0,0 +1,28 @@
import { flatten } from '../array/flatten.ts';
/**
* Creates a function that invokes `func` with arguments arranged according to the specified `indexes`
* where the argument value at the first index is provided as the first argument,
* the argument value at the second index is provided as the second argument, and so on.
*
* @template F The type of the function to re-arrange.
* @param {F} func The function to rearrange arguments for.
* @param {Array<number | number[]>} indexes The arranged argument indexes.
* @returns {(...args: any[]) => ReturnType<F>} Returns the new function.
*/
export function rearg<F extends (...args: any[]) => any>(
func: F,
...indexes: Array<number | number[]>
): (...args: any[]) => ReturnType<F> {
const flattenIndexes = flatten(indexes);
return function (this: any, ...args: any[]) {
const reorderedArgs: any[] = flattenIndexes.map(i => args[i]).slice(0, args.length);
for (let i = reorderedArgs.length; i < args.length; i++) {
reorderedArgs.push(args[i]);
}
return func.apply(this, reorderedArgs);
};
}