Repository: char0n/ramda-adjunct (js)

Issue:
rangeStep
**Is your feature request related to a problem? Please describe.**

```js
const rangeStep = R.curry((start, stop, step) => R.map(
  n => start + step * n,
  R.range(0, (1 + (stop - start) / step) >>> 0)
));
```

**Describe the solution you'd like**
```js
rangeStep(1, 4, 1);   // [1, 2, 3, 4]
rangeStep(2, 8, 2);   // [2, 4, 6, 8]
rangeStep(0, 10, 3);  // [0, 3, 6, 9]
rangeStep(3, -3, -2); // [3, 1, -1, -3]
```

**Additional context**

This function was described in [ramda cookbook](https://github.com/ramda/ramda/wiki/Cookbook#create-an-incrementing-or-decrementing-range-of-numbers-with-a-step)

- [ ] imlement the function
- [ ] add ref to ramda cookbook

Pull request description:
feat(list): add rangeStep
Refs #1477
Closes #1230

Patch:
diff --git a/src/index.js b/src/index.js
index 4ba3e27cf1..a11cda7c25 100644
--- a/src/index.js
+++ b/src/index.js
@@ -155,6 +155,7 @@ export { default as allUnique } from './allUnique';
 export { default as notAllUnique } from './notAllUnique';
 export { default as sortByProps } from './sortByProps';
 export { default as skipTake } from './skipTake';
+export { default as rangeStep } from './rangeStep';
 // Object
 export { default as invokeArgs } from './invokeArgs';
 export { default as paths } from './paths';
diff --git a/src/rangeStep.js b/src/rangeStep.js
new file mode 100644
index 0000000000..82c2d27545
--- /dev/null
+++ b/src/rangeStep.js
@@ -0,0 +1,37 @@
+import { curryN, map, range, always } from 'ramda';
+
+import floor from './floor';
+
+/**
+ * Creates an array of numbers (positive and/or negative) progressing from start up to, but not including, end.
+ *
+ * `Note`: JavaScript follows the IEEE-754 standard for resolving floating-point values which can produce unexpected results.
+ *
+ * @func rangeStep
+ * @memberOf RA
+ * @category List
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.30.0|v2.30.0}
+ * @sig Number -> Number -> Number -> [Number]
+ * @param {number} step The value to increment or decrement by
+ * @param {number} from The start of the range
+ * @param {number} to The end of the range
+ * @return {number[]} Returns the range of numbers
+ * @see {@link http://ramdajs.com/docs/#range|R.range}
+ * @example
+ *
+ * RA.rangeStep(1, 0, 4); // => [0, 1 ,2, 3]
+ * RA.rangeStep(-1, 0, -4); // => [0, -1, -2, -3]
+ * RA.rangeStep(1, 1, 5); // => [1, 2, 3, 4]
+ * RA.rangeStep(5, 0, 20); // => [0, 5, 10, 15]
+ * RA.rangeStep(-1, 0, -4); // => [0, -1, -2, -3]
+ * RA.rangeStep(0, 1, 4); // => [1, 1, 1]
+ * RA.rangeStep(1, 0, 0); // => []
+ */
+const rangeStep = curryN(3, (step, from, to) => {
+  const callback = step === 0 ? always(from) : (n) => from + step * n;
+  const rangeEnd = step === 0 ? to - from : floor((to - from) / step);
+
+  return map(callback, range(0, rangeEnd));
+});
+
+export default rangeStep;
diff --git a/types/index.d.ts b/types/index.d.ts
index 490ad736a5..93bf8b39fc 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1445,6 +1445,18 @@ declare namespace RamdaAdjunct {
          */
         toUinteger32(val: number): number;
         toUint32(val: number): number; // alias
+
+        /**
+         * Creates an array of numbers (positive and/or negative) progressing from start up to, but not including, end.
+         *
+         * `Note`: JavaScript follows the IEEE-754 standard for resolving floating-point values which can produce unexpected results.
+         */
+        rangeStep(step: number, from: number, to: number): number[];
+        rangeStep(step: number, from: number): (to: number) => number[];
+        rangeStep(step: number): {
+            (from: number, to: number): number[];
+            (from: number): (to: number) => number[];
+        };
     }
 }
 


Test patch:
diff --git a/test/rangeStep.js b/test/rangeStep.js
new file mode 100644
index 0000000000..30742f6e4d
--- /dev/null
+++ b/test/rangeStep.js
@@ -0,0 +1,25 @@
+import { assert } from 'chai';
+
+import * as RA from '../src';
+
+describe('rangeStep', function () {
+  it('should generate proper range', function () {
+    assert.deepEqual(RA.rangeStep(1, 0, 4), [0, 1, 2, 3]);
+    assert.deepEqual(RA.rangeStep(-1, 0, -4), [0, -1, -2, -3]);
+    assert.deepEqual(RA.rangeStep(1, 1, 5), [1, 2, 3, 4]);
+    assert.deepEqual(RA.rangeStep(5, 0, 20), [0, 5, 10, 15]);
+    assert.deepEqual(RA.rangeStep(-1, 0, -4), [0, -1, -2, -3]);
+    assert.deepEqual(RA.rangeStep(0, 1, 4), [1, 1, 1]);
+    assert.deepEqual(RA.rangeStep(0, -1, 4), [-1, -1, -1, -1, -1]);
+    assert.deepEqual(RA.rangeStep(-1, -3, -6), [-3, -4, -5]);
+    assert.deepEqual(RA.rangeStep(1, -1, 1), [-1, 0]);
+    assert.isEmpty(RA.rangeStep(1, 0, 0));
+  });
+
+  it('should curry', function () {
+    assert.deepEqual(RA.rangeStep(1, 0, 4), [0, 1, 2, 3]);
+    assert.deepEqual(RA.rangeStep(1)(0, 4), [0, 1, 2, 3]);
+    assert.deepEqual(RA.rangeStep(1, 0)(4), [0, 1, 2, 3]);
+    assert.deepEqual(RA.rangeStep(1)(0)(4), [0, 1, 2, 3]);
+  });
+});
diff --git a/types/test/rangeStep.ts b/types/test/rangeStep.ts
new file mode 100644
index 0000000000..de2fa35886
--- /dev/null
+++ b/types/test/rangeStep.ts
@@ -0,0 +1,13 @@
+import * as RA from 'ramda-adjunct';
+
+RA.rangeStep(1, 0, 4); // $ExpectType number[]
+RA.rangeStep(-1, -4, 0); // $ExpectType number[]
+RA.rangeStep(1, 1, 5); // $ExpectType number[]
+RA.rangeStep(5, 0, 20); // $ExpectType number[]
+RA.rangeStep(-1, 0, -4); // $ExpectType number[]
+RA.rangeStep(0, 1, 4); // $ExpectType number[]
+RA.rangeStep(1, 0, 0); // $ExpectType number[]
+
+RA.rangeStep(1, 0)(4); // $ExpectType number[]
+RA.rangeStep(1)(0, 4); // $ExpectType number[]
+RA.rangeStep(1)(0)(4); // $ExpectType number[]