npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@snigo.dev/range

v0.0.6

Published

Range class somewhat influenced by the function of the same name from Python

Downloads

3

Readme

Range

Usage

In the terminal:


$ npm install @snigo.dev/range

Then in the module:

// JavaScript modules
import Range from '@snigo.dev/range';

const range = new Range(-1, 1, 0.01);

Motivation

Range class is somewhat influenced by the function of the same name from Python. The important difference is that both start and end arguments are inclusive!


const range = new Range(1, 2);
range.toArray(); // [1, 2]

There is no particular reason behind it, I just wanted to humanize behavior.

Why do we need Range type? If you think about it we use ranges all the time. In particular I've created it as a helper type for my Color library, here are some usage examples:


const hueRange = new Range(359);
const percentRange = new Range(0, 1, 0.0001);

// Ensure hue value is always  in [0 ... 359] range
// (This is default browser behavior)
const hue = -45;
hueRange.mod(hue); // 315

// Ensure percentage value is clamped in [0 ... 1] range
const percentage = 1.25;
percentRange.clamp(percentage); // 1

Another useful case is just to generate array of numbers with provided step:


[...new Range(9)]; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[...new Range(3, -1)]; // [3, 2, 1, 0, -1]
[...new Range(1, -1, 0.25)]; // [1, 0.75, 0.5, 0.25, 0, -0.25, -0.5, -0.75, -1]

More examples below in the API section.

API

Constructor

Creates a new Range instance. Parameters: | Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | rangeStart | number | 0 | Defaults to zero if only one argument provided | | rangeEnd | number | | rangeEnd is inclusive in the range | | step | number | 1 | Cannot be less or equal than 0 |


new Range(-1, 1, 0.01); // Range{ from: -1, to: 1, step: 0.01 }
new Range(-1, 1); // Range{ from: -1, to: 1, step: 1 }
new Range(100); // Range{ from: 0, to: 100, step: 1 }
new Range(); // Range{}

*NOTE: because Range.from and Range.to are immutable properties, initializing an empty range (without arguments) would be practicaly useless.

Properties

Range.length

Returns total number of numbers in the range, if range will be converted to the array


const range = new Range(0, 100, 0.01);
range.length; // 10001

const array = [...range];
array.length; // 10001

It is useful to check the length prior to converting range into array:


const range = new Range(Infinity);
range.length; // Infinity

[...range]; // Throws error: Cannot iterate through infinite range

Range.from

Range.to

Range.step

Return initial values used to create the range:


const range = new Range(0, 100, 0.01);
range.from; // 0
range.to; // 100
range.step; // 0.01

Range.max

Range.min

Range.center

Return the number from the range with the greatest value, the least value and the center of the range


const range = new Range(5, -5);
range.min; // -5
range.max; // 5
range.center; // 0

Note: All properties of the Range instance are read-only

Methods

Range.from()

Static method on Range to generate range from array or any other iterable type. Returns a new Range instance.

| Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | iterable | any | | Any iterable type, like Array, Set, String... |


const arr = [7, 6, 5, 42, 16, 9];
Range.from(arr); // Range{ from: 5, to: 42, step: 1 }

const str = '420';
// By no way I encourage you to do this! :)
Range.from(str); // Range{ from: 0, to: 4, step: 1 }

Range.from([]); // Range{}

Range.prototype.clamp()

Similarly to _.clamp(), ensures the resulting number is in the range. Returns clamped number.

| Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | number | number | | |


const range = new Range(99);
range.clamp(100); // 99
range.clamp(-42); // 0
range.clamp(15); // 15
range.clamp(); // NaN

Range.prototype.has()

Returns boolean indicating whether provided number within a range.

| Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | number | number | | |


const range = new Range(99);
range.has(100); // false
range.has(-42); // false
range.has(15); // true
range.has(); // false

Range.prototype.forEach()

Executes provided function on every element of the range with the provided step. If no step provided initial Range.step will be used. Similarly to forEach in Array, following arguments will be passed to the callback:

  • currentValue
  • index
  • range

Will try to account for precision errors between numbers in the range. Always returns undefined.

| Parameter | Type | Default value | Notes | |---------------|-----------|-------------------|------------------------------------------------| | fn |function | | To be invoked as fn(number, index, range) | | step |number | Range.step | forEach() can be invoked with custom step |


const range = new Range(-1, 1);
range.forEach((number) => {
  console.log(number);
}, 0.5);
// -1
// -0.5
// 0
// 0.5
// 1

Range.prototype.forEachReverse()

Similarly to Range.prototype.forEach(), executes provided function on every element of the range with the provided step in reversed manner. If no step provided initial Range.step will be used. Similarly to forEach in Array, following arguments will be passed to the callback:

  • currentValue
  • index
  • range

Will try to account for precision errors between numbers in the range. Always returns undefined.

| Parameter | Type | Default value | Notes | |---------------|-----------|-------------------|--------------------------------------------------| | fn |function | | To be invoked as fn(number, index, range) | | step |number | Range.step | forEachReverse() can be invoked with custom step |


const range = new Range(-1, 1);
range.forEachReverse((number) => {
  console.log(number);
}, 0.5);
// 1
// 0.5
// 0
// -0.5
// -1

Range.prototype.getFraction()

Returns ratio of the provided number proportional to the range.

| Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | number | number | | | | precision | number | 12 | Optional |

Useful to calculate percentage value of the range.


const range = new Range(255);
range.getFraction(17, 4); // 0.0667
// Value 17 represents 6.67% of the [0 ... 255] range

Range.prototype.fromFraction()

Inverse method from Range.prototype.getFraction(). Returns number represented by provided ratio relative to the range.

| Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | ratio | number | | | | precision | number | 12 | Optional |

Useful to calculate value from the percentage.


const range = new Range(255);
range.fromFraction(0.065, 0); // 17
// Value 17 represents 6.5% of the [0 ... 255] range with 0 floating point precision

Range.prototype.toArray()

Converts range to array. Throws error with range length is greater than max possible array size.


const range = new Range(0.1, 0.5, 0.1);
range.toArray(); // [0.1, 0.2, 0.3, 0.4, 0.5]
// same as [...range]

const infiniteRange = new Range(0.1, 0.5, Number.MIN_VALUE);
infiniteRange.length; // Infinity
range.toArray(); // throws Error: Cannot iterate infinite size range

Range.prototype.mod()

Returns number in the range which is the result of modulo operation of the provided input number to the range.

| Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | number | number | | |

It's really much easier to explain on the example:


const range = new Range(359);
range.mod(360); // 0
range.mod(-45); // 315
range.mod(15); // 15
range.mod(89352); // 72

Range.prototype.slice()

Slices range into the provided number of equal parts. Returns array of numbers representing starting boundaries of each such slice.

| Parameter | Type | Default value | Notes | |---------------|----------|-------------------|------------------------------------------------| | parts | number | | Cannot be negative number |


const range = new Range(359);
range.slice(6); // [0, 60, 120, 180, 240, 300]
range.slice(9); // [0, 40, 80, 120, 160, 200, 240, 280, 320]
range.slice(12); // [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]

TODOs

  1. Introduce optional Range Configurator argument:
const range = new Range(10, { trailing: false });
[...range]; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. Rewrite has() to be step-dependent:
const range = new Range(0, 10, 0.5, { trailing: false });
range.has(5); // true
range.has(4.5); // true
range.has(3.14); // false

Current has() functionality will move to another method, probably within() or something like that. Reasoning: range could not only represent the range itself but also a set of numbers.

  1. Rewrite step logic so it can optionally take a function as a value. This function will receive last step and value during iteration and return value of this function will produce current step of iteration.

Disadvantage of such implementation would be linear length which puts a lot of question marks on dealing with potentially infinite ranges.

  1. Introduce step-dependent rounding methods:
  • round(number)
  • floor(number)
  • ceil(number) All three will return number rounded to closest number in range set.
  1. Step-dependent indexOf(): returns index of value within the range as if range was converted to an array.

  2. Introduce iterator-based values(mapFn) method that will return the iterator object containing values for each number within the range. Optional mapFn, as name suggests, will be applied to each number returning new value.

  3. Range.prototype.place(extRange, extValue): returns value within current range corresponding to external value withing external range as if it would be scaled to current one.