E52D feat(range): add step parameter to range function by dev-oil · Pull Request #1257 · remeda/remeda · GitHub
[go: up one dir, main page]

Skip to content

Conversation

@dev-oil
Copy link
@dev-oil dev-oil commented Jan 25, 2026

Summary

Adds an optional step parameter to the range function, similar to lodash's implementation.

Changes

  • Added range(start, end, step) signature
  • Supports both positive and negative step values
  • Maintains backward compatibility with existing range(start, end) usage
  • Added comprehensive tests

Examples

R.range(1, 20, 5) // => [1, 6, 11, 16]
R.range(20, 1, -5) // => [20, 15, 10, 5]
R.range(0, 10, 2) // => [0, 2, 4, 6, 8]

Checklist

  • Typedoc added for new methods and updated for changed
  • Tests added for new methods and updated for changed
  • New methods added to src/index.ts (not needed - existing function)
  • New methods added to docs/src/content/mapping (already exists)

- Add optional step parameter to range function (similar to lodash)
- Support positive and negative step values
- Maintain backward compatibility with existing range(start, end) signature
- Add tests for step functionality
@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@netlify
Copy link
netlify bot commented Jan 25, 2026

Deploy Preview for trusting-lumiere-9c7fad ready!

Name Link
🔨 Latest commit 61f6a51
🔍 Latest deploy log https://app.netlify.com/projects/trusting-lumiere-9c7fad/deploys/69772cdb471d0200081d1686
😎 Deploy Preview https://deploy-preview-1257--trusting-lumiere-9c7fad.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Member
@eranhirsch eranhirsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey and thanks for sending this PR out. This is a cool change and I would love to add it to the library.

My biggest concern at the moment though is that we have a mismatch between the data-first and the data-last signatures. I understand that this might have been due to the fact that in the way this function is currently written there's no way to differentiate a data-first invocation without a step param and a data-last invocation with one (e.g. they are both range(number, number)).

I think the better API here would be to expand the type for the second param (which is currently used for end) to support both cases by typing it as number | RangeOptions where:

type RangeOptions = {
  readonly end: number;
  readonly step?: number;
}

This would allow us to use the regular purry function for purrying without any modifications to it's regular flow, and only add a single line to the implementation itself to differentiate between being provided an options object or a primitive number. e.g.

function rangeImplementation(
  start: number,
  endOrOptions: number | RangeOptions,
): number[] {
  const { end, step = 1 } = typeof endOrOptions === "number" ? { end: endOrOptions } : endOrOptions;
  ...
}

test("range with step 2", () => {
expect(range(0, 10, 2)).toStrictEqual([0, 2, 4, 6, 8]);
});
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to have some additional tests:

  • step=0.
  • step is non-integer (e.g. 2.5).
  • step is in wrong direction to start and stop (e.g. range(1,10,-1))

step: number,
): number[] {
if (step === 0) {
throw new Error("Step cannot be zero");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error type here should be RangeError.

Suggested change
throw new Error("Step cannot be zero");
throw new RangeError(`range: start=${start} and step=${step} would result in an infinite range!`);

end: number,
step: number,
): number[] {
if (step === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of JavaScript's floating-point arithmetics, the more accurate test here would be to check if steps cause a progression, regardless of it being 0, e.g.

Suggested change
if (step === 0) {
if (start + step === start) {

Comment on lines +75 to 83
if (step > 0) {
for (let i = start; i < end; i += step) {
ret.push(i);
}
} else {
for (let i = start; i > end; i += step) {
ret.push(i);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can be more concise here:

Suggested change
if (step > 0) {
for (let i = start; i < end; i += step) {
ret.push(i);
}
} else {
for (let i = start; i > end; i += step) {
ret.push(i);
}
}
for (let i = start; step > 0 ? i < end : i > end; i += step) {
ret.push(i);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0