remeda
Version:
A utility library for JavaScript and Typescript.
1 lines • 4.19 kB
Source Map (JSON)
{"version":3,"file":"range.cjs","names":["purry"],"sources":["../src/range.ts"],"sourcesContent":["import { purry } from \"./purry\";\n\nconst DEFAULT_STEP = 1;\n\n// Relative tolerance for snapping a near-integer division result to the nearest\n// integer, preventing Math.ceil from inflating the length by one due to\n// floating-point error in the numerator or denominator.\nconst SNAP_TOLERANCE = 1e-12;\n\ntype RangeOptions = {\n readonly end: number;\n readonly step: number;\n};\n\n// TODO [>2]: Change the signature so that range takes a required \"end\" parameter and an optional Options object with `start` and `step` defaulting to `0` and `1` respectively to better align with how `range` works in other libraries and languages.\n/**\n * Returns a sequence of numbers from `start` (inclusive) to `end` (exclusive),\n * adding `step` (default is `1`) to each number in the sequence.\n *\n * @param start - The first number in the sequence.\n * @param endOrOptions - The end number **or** an object which is used to\n * **also** define a step size.\n * @param endOrOptions.end - The non-inclusive end of the range.\n * @param endOrOptions.step - The gap between consecutive numbers.\n * @signature\n * range(start, end)\n * range(start, { end, step })\n * @example\n * range(1, 5); //=> [1, 2, 3, 4]\n * range(1, { end: 5, step: 2 }); //=> [1, 3]\n * @dataFirst\n * @category Array\n */\nexport function range(\n start: number,\n endOrOptions: number | RangeOptions,\n): number[];\n\n/**\n * Returns a sequence of numbers from `start` (inclusive) to `end` (exclusive),\n * adding `step` (default is `1`) to each number in the sequence.\n *\n * @param endOrOptions - The end number **or** an object which is used to\n * **also** define a step size.\n * @param endOrOptions.end - The non-inclusive end of the range.\n * @param endOrOptions.step - The gap between consecutive numbers.\n * @signature\n * range(end)(start)\n * range({ end, step })(start)\n * @example\n * pipe(1, range(5)); //=> [1, 2, 3, 4]\n * pipe(1, range({ end: 5, step: 2 })); //=> [1, 3]\n * @dataLast\n * @category Array\n */\nexport function range(\n endOrOptions: number | RangeOptions,\n): (start: number) => number[];\n\nexport function range(...args: readonly unknown[]): unknown {\n return purry(rangeImplementation, args);\n}\n\nfunction rangeImplementation(\n start: number,\n endOrOptions: number | RangeOptions,\n): number[] {\n const step =\n typeof endOrOptions === \"object\" ? endOrOptions.step : DEFAULT_STEP;\n if (step === 0) {\n throw new RangeError(\"range: step cannot be zero (0)!\");\n }\n\n const end =\n typeof endOrOptions === \"object\" ? endOrOptions.end : endOrOptions;\n const length = ceilingWithSnap((end - start) / step);\n if (length <= 0) {\n return [];\n }\n\n return Array.from({ length }, (_, i) => (i === 0 ? start : start + i * step));\n}\n\n// JS's floating-point math can create numbers that are slightly larger than\n// the true mathematical result (e.g. `0.1 + 0.2 > 0.3`). This error would\n// propagate into more complex calculations, and specifically can cause\n// the built-in `Math.ceil` to round up a number that is effectively an\n// integer (e.g. `Math.ceil(0.1 + 0.2 - 0.3) === 1`). To work around this we\n// need an error margin where ceiling would ignore very small floating point\n// artifacts so that it effectively \"rounds\" down instead of up.\nfunction ceilingWithSnap(raw: number): number {\n if (raw === 0) {\n return 0;\n }\n\n const rounded = Math.round(raw);\n return Math.abs(raw - rounded) / Math.abs(raw) < SNAP_TOLERANCE\n ? rounded\n : Math.ceil(raw);\n}\n"],"mappings":"kGA2DA,SAAgB,EAAM,GAAG,EAAmC,CAC1D,OAAOA,EAAAA,MAAM,EAAqB,CAAI,CACxC,CAEA,SAAS,EACP,EACA,EACU,CACV,IAAM,EACJ,OAAO,GAAiB,SAAW,EAAa,KAAO,EACzD,GAAI,IAAS,EACX,MAAU,WAAW,iCAAiC,EAKxD,IAAM,EAAS,IADb,OAAO,GAAiB,SAAW,EAAa,IAAM,GAClB,GAAS,CAAI,EAKnD,OAJI,GAAU,EACL,CAAC,EAGH,MAAM,KAAK,CAAE,QAAO,GAAI,EAAG,IAAO,IAAM,EAAI,EAAQ,EAAQ,EAAI,CAAK,CAC9E,CASA,SAAS,EAAgB,EAAqB,CAC5C,GAAI,IAAQ,EACV,MAAO,GAGT,IAAM,EAAU,KAAK,MAAM,CAAG,EAC9B,OAAO,KAAK,IAAI,EAAM,CAAO,EAAI,KAAK,IAAI,CAAG,EAAI,MAC7C,EACA,KAAK,KAAK,CAAG,CACnB"}