-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(compat): implement range/rangeRight (#881)
* feat(compat): implement range/rangeRight * make lint happy
- Loading branch information
Showing
7 changed files
with
339 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |