mirror of
https://github.com/toss/es-toolkit.git
synced 2024-12-27 09:53:34 +03:00
feat(compat): implement range/rangeRight (#881)
* feat(compat): implement range/rangeRight * make lint happy
This commit is contained in:
parent
4cc2a895c1
commit
99f7611e4b
@ -1,8 +1,10 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { range as rangeToolkit_ } from 'es-toolkit';
|
||||
import { range as rangeCompatToolkit_ } from 'es-toolkit/compat';
|
||||
import { range as rangeLodash_ } from 'lodash';
|
||||
|
||||
const rangeToolkit = rangeToolkit_;
|
||||
const rangeCompatToolkit = rangeCompatToolkit_;
|
||||
const rangeLodash = rangeLodash_;
|
||||
|
||||
describe('range', () => {
|
||||
@ -10,6 +12,10 @@ describe('range', () => {
|
||||
rangeToolkit(0, 100, 1);
|
||||
});
|
||||
|
||||
bench('es-toolkit/compat/range', () => {
|
||||
rangeCompatToolkit(0, 100, 1);
|
||||
});
|
||||
|
||||
bench('lodash/range', () => {
|
||||
rangeLodash(0, 100, 1);
|
||||
});
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { rangeRight as rangeRightToolkit_ } from 'es-toolkit';
|
||||
import { rangeRight as rangeRightCompatToolkiTt_ } from 'es-toolkit/compat';
|
||||
import { rangeRight as rangeRightLodash_ } from 'lodash';
|
||||
|
||||
const rangeRightToolkit = rangeRightToolkit_;
|
||||
const rangeRightCompatToolkit = rangeRightCompatToolkiTt_;
|
||||
const rangeRightLodash = rangeRightLodash_;
|
||||
|
||||
describe('rangeRight', () => {
|
||||
@ -10,6 +12,10 @@ describe('rangeRight', () => {
|
||||
rangeRightToolkit(0, 100, 1);
|
||||
});
|
||||
|
||||
bench('es-toolkit/compat/rangeRight', () => {
|
||||
rangeRightCompatToolkit(0, 100, 1);
|
||||
});
|
||||
|
||||
bench('lodash/rangeRight', () => {
|
||||
rangeRightLodash(0, 100, 1);
|
||||
});
|
||||
|
@ -104,6 +104,8 @@ export { max } from './math/max.ts';
|
||||
export { min } from './math/min.ts';
|
||||
export { parseInt } from './math/parseInt.ts';
|
||||
export { random } from './math/random.ts';
|
||||
export { range } from './math/range.ts';
|
||||
export { rangeRight } from './math/rangeRight.ts';
|
||||
export { round } from './math/round.ts';
|
||||
export { sum } from './math/sum.ts';
|
||||
export { sumBy } from './math/sumBy.ts';
|
||||
|
72
src/compat/math/range.spec.ts
Normal file
72
src/compat/math/range.spec.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { each, map } from '..';
|
||||
import { range } from './range';
|
||||
import { falsey } from '../_internal/falsey';
|
||||
|
||||
describe('range', () => {
|
||||
it(`\`_.range\` should infer the sign of \`step\` when only \`end\` is given`, () => {
|
||||
expect(range(4)).toEqual([0, 1, 2, 3]);
|
||||
expect(range(-4)).toEqual([0, -1, -2, -3]);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should infer the sign of \`step\` when only \`start\` and \`end\` are given`, () => {
|
||||
expect(range(1, 5)).toEqual([1, 2, 3, 4]);
|
||||
expect(range(5, 1)).toEqual([5, 4, 3, 2]);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should work with a \`start\`, \`end\`, and \`step\``, () => {
|
||||
expect(range(0, -4, -1)).toEqual([0, -1, -2, -3]);
|
||||
expect(range(5, 1, -1)).toEqual([5, 4, 3, 2]);
|
||||
expect(range(0, 20, 5)).toEqual([0, 5, 10, 15]);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should support a \`step\` of \`0\``, () => {
|
||||
expect(range(1, 4, 0)).toEqual([1, 1, 1]);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should work with a \`step\` larger than \`end\``, () => {
|
||||
expect(range(1, 5, 20)).toEqual([1]);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should work with a negative \`step\``, () => {
|
||||
expect(range(0, -4, -1)).toEqual([0, -1, -2, -3]);
|
||||
expect(range(21, 10, -3)).toEqual([21, 18, 15, 12]);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should support \`start\` of \`-0\``, () => {
|
||||
const actual = range(-0, 1);
|
||||
expect(1 / actual[0]).toBe(-Infinity);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should treat falsey \`start\` as \`0\``, () => {
|
||||
each(falsey, (value, index) => {
|
||||
if (index) {
|
||||
// @ts-expect-error - invalid arguments
|
||||
expect(range(value)).toEqual([]);
|
||||
// @ts-expect-error - invalid arguments
|
||||
expect(range(value, 1)).toEqual([0]);
|
||||
} else {
|
||||
// @ts-expect-error - invalid arguments
|
||||
expect(range()).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it(`\`_.range\` should coerce arguments to finite numbers`, () => {
|
||||
// @ts-expect-error - invalid arguments
|
||||
const actual = [range('1'), range('0', 1), range(0, 1, '1'), range(NaN), range(NaN, NaN)];
|
||||
|
||||
expect(actual).toEqual([[0], [0], [0], [], []]);
|
||||
});
|
||||
|
||||
it(`\`_.range\` should work as an iteratee for methods like \`_.map\``, () => {
|
||||
const array = [1, 2, 3];
|
||||
const object = { a: 1, b: 2, c: 3 };
|
||||
const expected = [[0], [0, 1], [0, 1, 2]];
|
||||
|
||||
each([array, object], collection => {
|
||||
const actual = map(collection, range);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
90
src/compat/math/range.ts
Normal file
90
src/compat/math/range.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { isIterateeCall } from '../_internal/isIterateeCall.ts';
|
||||
import { toFinite } from '../util/toFinite.ts';
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `0` (inclusive) to `end` (exclusive), incrementing by `1`.
|
||||
*
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @returns {number[]} An array of numbers from `0` (inclusive) to `end` (exclusive) with a step of `1`.
|
||||
*
|
||||
* @example
|
||||
* // Returns [0, 1, 2, 3]
|
||||
* range(4);
|
||||
*/
|
||||
export function range(end: number): number[];
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `start` (inclusive) to `end` (exclusive), incrementing by `1`.
|
||||
*
|
||||
* @param {number} start - The starting number of the range (inclusive).
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @returns {number[]} An array of numbers from `start` (inclusive) to `end` (exclusive) with a step of `1`.
|
||||
*
|
||||
* @example
|
||||
* // Returns [1, 2, 3]
|
||||
* range(1, 4);
|
||||
*/
|
||||
export function range(start: number, end: number): number[];
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `start` (inclusive) to `end` (exclusive), incrementing by `step`.
|
||||
*
|
||||
* @param {number} start - The starting number of the range (inclusive).
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @param {number} step - The step value for the range.
|
||||
* @returns {number[]} An array of numbers from `start` (inclusive) to `end` (exclusive) with the specified `step`.
|
||||
*
|
||||
* @example
|
||||
* // Returns [0, 5, 10, 15]
|
||||
* range(0, 20, 5);
|
||||
*/
|
||||
export function range(start: number, end: number, step: number): number[];
|
||||
|
||||
/**
|
||||
* Enables use as an iteratee for methods like `_.map`.
|
||||
*
|
||||
* @param {number} end - The current iteratee value.
|
||||
* @param {PropertyKey} index - The iteration index.
|
||||
* @param {object} guard - The iteratee object.
|
||||
* @returns {number[]} An array of numbers from `start` (inclusive) to `end` (exclusive) with the specified `step`.
|
||||
*/
|
||||
export function range(end: number, index: PropertyKey, guard: object): number[];
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `start` (inclusive) to `end` (exclusive), incrementing by `step`.
|
||||
*
|
||||
* @param {number} start - The starting number of the range (inclusive).
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @param {number} step - The step value for the range.
|
||||
* @returns {number[]} An array of numbers from `start` (inclusive) to `end` (exclusive) with the specified `step`.
|
||||
*
|
||||
* @example
|
||||
* // Returns [0, 1, 2, 3]
|
||||
* range(4);
|
||||
*
|
||||
* @example
|
||||
* // Returns [0, -1, -2, -3]
|
||||
* range(0, -4, -1);
|
||||
*/
|
||||
export function range(start: number, end?: PropertyKey, step?: any): number[] {
|
||||
// Enables use as an iteratee for methods like `_.map`.
|
||||
if (step && typeof step !== 'number' && isIterateeCall(start, end, step)) {
|
||||
end = step = undefined;
|
||||
}
|
||||
start = toFinite(start);
|
||||
if (end === undefined) {
|
||||
end = start;
|
||||
start = 0;
|
||||
} else {
|
||||
end = toFinite(end);
|
||||
}
|
||||
step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);
|
||||
|
||||
const length = Math.max(Math.ceil((end - start) / (step || 1)), 0);
|
||||
const result = new Array(length);
|
||||
for (let index = 0; index < length; index++) {
|
||||
result[index] = start;
|
||||
start += step;
|
||||
}
|
||||
return result;
|
||||
}
|
72
src/compat/math/rangeRight.spec.ts
Normal file
72
src/compat/math/rangeRight.spec.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { each, map } from '..';
|
||||
import { rangeRight } from './rangeRight';
|
||||
import { falsey } from '../_internal/falsey';
|
||||
|
||||
describe('rangeRight methods', () => {
|
||||
it(`\`_.rangeRightRight\` should infer the sign of \`step\` when only \`end\` is given`, () => {
|
||||
expect(rangeRight(4)).toEqual([0, 1, 2, 3].reverse());
|
||||
expect(rangeRight(-4)).toEqual([0, -1, -2, -3].reverse());
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should infer the sign of \`step\` when only \`start\` and \`end\` are given`, () => {
|
||||
expect(rangeRight(1, 5)).toEqual([1, 2, 3, 4].reverse());
|
||||
expect(rangeRight(5, 1)).toEqual([5, 4, 3, 2].reverse());
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should work with a \`start\`, \`end\`, and \`step\``, () => {
|
||||
expect(rangeRight(0, -4, -1)).toEqual([0, -1, -2, -3].reverse());
|
||||
expect(rangeRight(5, 1, -1)).toEqual([5, 4, 3, 2].reverse());
|
||||
expect(rangeRight(0, 20, 5)).toEqual([0, 5, 10, 15].reverse());
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should support a \`step\` of \`0\``, () => {
|
||||
expect(rangeRight(1, 4, 0)).toEqual([1, 1, 1].reverse());
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should work with a \`step\` larger than \`end\``, () => {
|
||||
expect(rangeRight(1, 5, 20)).toEqual([1]);
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should work with a negative \`step\``, () => {
|
||||
expect(rangeRight(0, -4, -1)).toEqual([0, -1, -2, -3].reverse());
|
||||
expect(rangeRight(21, 10, -3)).toEqual([21, 18, 15, 12].reverse());
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should support \`start\` of \`-0\``, () => {
|
||||
const actual = rangeRight(-0, 1);
|
||||
expect(1 / actual[0]).toBe(-Infinity);
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should treat falsey \`start\` as \`0\``, () => {
|
||||
each(falsey, (value, index) => {
|
||||
if (index) {
|
||||
// @ts-expect-error - invalid arguments
|
||||
expect(rangeRight(value)).toEqual([]);
|
||||
// @ts-expect-error - invalid arguments
|
||||
expect(rangeRight(value, 1)).toEqual([0]);
|
||||
} else {
|
||||
// @ts-expect-error - invalid arguments
|
||||
expect(rangeRight()).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should coerce arguments to finite numbers`, () => {
|
||||
// @ts-expect-error - invalid arguments
|
||||
const actual = [rangeRight('1'), rangeRight('0', 1), rangeRight(0, 1, '1'), rangeRight(NaN), rangeRight(NaN, NaN)];
|
||||
|
||||
expect(actual).toEqual([[0], [0], [0], [], []]);
|
||||
});
|
||||
|
||||
it(`\`_.rangeRight\` should work as an iteratee for methods like \`_.map\``, () => {
|
||||
const array = [1, 2, 3];
|
||||
const object = { a: 1, b: 2, c: 3 };
|
||||
const expected = [[0], [0, 1].reverse(), [0, 1, 2].reverse()];
|
||||
|
||||
each([array, object], collection => {
|
||||
const actual = map(collection, rangeRight);
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
91
src/compat/math/rangeRight.ts
Normal file
91
src/compat/math/rangeRight.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { isIterateeCall } from '../_internal/isIterateeCall.ts';
|
||||
import { toFinite } from '../util/toFinite.ts';
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `end` (exclusive) to `0` (inclusive), decrementing by `1`.
|
||||
*
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @returns {number[]} An array of numbers from `end` (exclusive) to `0` (inclusive) with a step of `1`.
|
||||
*
|
||||
* @example
|
||||
* // Returns [3, 2, 1, 0]
|
||||
* rangeRight(4);
|
||||
*/
|
||||
export function rangeRight(end: number): number[];
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `end` (exclusive) to `start` (inclusive), decrementing by `1`.
|
||||
*
|
||||
* @param {number} start - The starting number of the range (inclusive).
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @returns {number[]} An array of numbers from `end` (exclusive) to `start` (inclusive) with a step of `1`.
|
||||
*
|
||||
* @example
|
||||
* // Returns [3, 2, 1]
|
||||
* rangeRight(1, 4);
|
||||
*/
|
||||
export function rangeRight(start: number, end: number): number[];
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `end` (exclusive) to `start` (inclusive), decrementing by `step`.
|
||||
*
|
||||
* @param {number} start - The starting number of the range (inclusive).
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @param {number} step - The step value for the range.
|
||||
* @returns {number[]} An array of numbers from `end` (exclusive) to `start` (inclusive) with the specified `step`.
|
||||
*
|
||||
* @example
|
||||
* // Returns [15, 10, 5, 0]
|
||||
* rangeRight(0, 20, 5);
|
||||
*/
|
||||
export function rangeRight(start: number, end: number, step: number): number[];
|
||||
|
||||
/**
|
||||
* Enables use as an iteratee for methods like `_.map`.
|
||||
*
|
||||
* @param {number} end - The current iteratee value.
|
||||
* @param {PropertyKey} index - The iteration index.
|
||||
* @param {object} guard - The iteratee object.
|
||||
* @returns {number[]} An array of numbers from `start` (inclusive) to `end` (exclusive) with the specified `step`.
|
||||
*/
|
||||
export function rangeRight(end: number, index: PropertyKey, guard: object): number[];
|
||||
|
||||
/**
|
||||
* Returns an array of numbers from `end` (exclusive) to `start` (inclusive), decrementing by `step`.
|
||||
*
|
||||
* @param {number} start - The starting number of the range (inclusive).
|
||||
* @param {number} end - The end number of the range (exclusive).
|
||||
* @param {number} step - The step value for the range.
|
||||
* @returns {number[]} An array of numbers from `end` (exclusive) to `start` (inclusive) with the specified `step`.
|
||||
* @throws {Error} Throws an error if the step value is not a non-zero integer.
|
||||
*
|
||||
* @example
|
||||
* // Returns [3, 2, 1, 0]
|
||||
* rangeRight(4);
|
||||
*
|
||||
* @example
|
||||
* // Returns [-3, -2, -1, 0]
|
||||
* rangeRight(0, -4, -1);
|
||||
*/
|
||||
export function rangeRight(start: number, end?: PropertyKey, step?: any): number[] {
|
||||
// Enables use as an iteratee for methods like `_.map`.
|
||||
if (step && typeof step !== 'number' && isIterateeCall(start, end, step)) {
|
||||
end = step = undefined;
|
||||
}
|
||||
start = toFinite(start);
|
||||
if (end === undefined) {
|
||||
end = start;
|
||||
start = 0;
|
||||
} else {
|
||||
end = toFinite(end);
|
||||
}
|
||||
step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);
|
||||
|
||||
const length = Math.max(Math.ceil((end - start) / (step || 1)), 0);
|
||||
const result = new Array(length);
|
||||
for (let index = length - 1; index >= 0; index--) {
|
||||
result[index] = start;
|
||||
start += step;
|
||||
}
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue
Block a user