ranging
TypeScript icon, indicating that this package has built-in type declarations

4.1.0 • Public • Published

ranging (JavaScript library). NEW Syntax available!!!

Range helper classes based on iterators

Installation

Using npm:

npm install ranging
# or
npm i ranging

Using yarn:

yarn add ranging

Important!

  • JavaScript supports only ES5 and newer
  • Typescript supports only for v1.1.0+
  • New Syntax supports only for v4.0.0+ (and chain mode also)

Examples

Importing

const { NumberRange, WalkerRange } = require('ranging');
// ES
import { NumberRange, WalkerRange } from 'ranging';

For NodeJS module you should use:

// I tried to realize it, but previous import does not work :(
// Use this please
import { NumberRange, WalkerRange } from 'ranging/index.mjs';

And you can use function analogs of classes:

import { zipRange, walkerRange, mergeRange } from 'ranging';

Number ranges

Integers

A list of integers from 1 to 10 inclusive

const { NumberRange } = require('ranging');

const sameIntegers = [...new NumberRange(1 /* from */, 10 /* to */)];

Integers ranged with step of 2

const integers = [
  ...new NumberRange(1 /* from */, 10 /* to */, 2 /* step */),
];
// integers: [ 1, 3, 5, 7, 9 ]

Floating point numbers

let floats = [
  ...new NumberRange(
    2,      // from
    5,      // to
    0.5,    // step
    true    // use exact addition?
  ),
];
console.log(floats);
// [ 2, 2.5, 3, 3.5, 4, 4.5, 5 ]

floats = [
  ...new NumberRange(2, 5, undefined, true),
];
console.log(floats);
// [ 2, 3, 4, 5 ]

Note: sum fractional floating point numbers with NumberRange.reduce(sum).
Approximation error

let sum = 0;
for (let i = 1; i <= 20; i++) {
  sum += i / 10;
}
console.log(sum);
// 20.999999999999996

Use of NumberRange.reduce(sum) (expected answer)

import { sum, NumberRange } from 'ranging';

console.log(
  ...new NumberRange(0.1, 2, 0.1).
    .reduce(sum),
);
// 21

Infinite generator

Use of NumberRange without specifying end

let counter = 0;
const result = [];

for (let i of new NumberRange(1005)) {
  const hex = i.toString(16);
  if (hex == hex.split('').reverse().join('')) {
    counter++;
    result.unshift(i);
  }
  if (counter === 10) break;
}
console.log(result);
// [ 1156, 1140, 1124, 1108, 1092, 1076, 1060, 1044, 1028, 1011 ]

Starting from 1005, executes until finds 10 numbers that are palindromes in hexadecimal notation. The result is presented in descending order.

Other standard operations

You can use the following operations: filter, map, reduce, take, from, to, find, count, combine, shuffle, groupBy, collect.

  • filter is needed to get only those elements that satisfy the predicate (returns range with filtered values).
  • map is needed to replace values to another (returns range with replaced values).
  • reduce is needed to calculate value using all values from range (returns range with calculated value).
  • take is needed to take only specified quantity of elements (returns range with values).
  • from is needed to take all the elements after the predicate becomes true (returns range with values).
  • to is needed to take all the elements before the predicate becomes true (returns range with values).
  • find is needed to find value by predicate (returns range with the founded value).
  • count is needed to find count of elements in range (returns range with count of elements).
  • combine is needed to combine elements into array (returns range of arrays of two different values).
  • shuffle is needed to shuffle current range, the picking parameter is used to indicate the "entropy": the larger it is, the more random, with picking === 1 shuffle does not work (returns range with shuffled values).
  • groupBy is needed to grouping elements by quantity (returns range with grouped values).
  • collect is used to collect all elements to array (returns array of elements from range).

Standard operations predicates and callbacks

filter, from, to, find take predicate with following signature:

type Predicate<T> = (value: T, index: number) => boolean; // where T is the type of elements in range

reduce takes callback with signature:

type Reducer<A, T> = (accamulator: A, value: T, index: number) => A;
// where T is the type of elements in range
// where A is the type of reduce operation result

map takes callback with default functor signature:

type Mapper<T, R> = (value: T, index: number) => R;
// where T is the type of elements in range
// where R is the type of map operation result

Usage

Every operation returns range with the same operations (except collect):

const { NumberRange } = require('ranging');

console.log(
  new NumberRange(0, 9)
    .map((el) => el ** 2)
    .reduce((acc, value) => acc + value.toString(), '')
    .collect(),
);

You can also use as many operations as you need:

console.log(
  new NumberRange(0, 9)
    .map((el) => el * 7)
    .filter((el) => el % 2 === 1)
    .map((el) => el * 21)
    .collect(),
);
// [147, 441, 735, 1029, 1323]

Walker Ranges

Iteration over a passed iterable element

const { WalkerRange } = require('ranging');

console.log([
  ...new WalkerRange('Hello world'),
]);
// ['H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']

console.log([
  ...new WalkerRange([1, 2, 3, 4, 5])
    .map((el) => el * 3)
    .filter((el) => el % 2 === 0),
])
// [6, 12]

Date Ranges

Simple Day Range

const { DayRange } = require('ranging');

console.log(
  new DayRange(new Date(), undefined, 5)
    .take(5)
    .collect(),
);

/*
    [
      2023-02-01T00:00:00.000Z,
      2023-02-06T00:00:00.000Z,
      2023-02-11T00:00:00.000Z,
      2023-02-16T00:00:00.000Z,
      2023-02-21T00:00:00.000Z
    ]
*/

Other time units

You can use various time units, such as SecondRange, MinuteRange and etc.

const { YearRange, isLeapYear } = require('ranging');

console.log([
  ...new YearRange()
    .filter(isLeapYear)
    .take(10)
])

// [
//   2024-07-08T22:17:30.270Z,
//   2028-07-08T22:17:30.270Z,
//   2032-07-08T22:17:30.270Z,
//   2036-07-08T22:17:30.270Z,
//   2040-07-08T22:17:30.270Z,
//   2044-07-08T22:17:30.270Z,
//   2048-07-08T22:17:30.270Z,
//   2052-07-08T22:17:30.270Z,
//   2056-07-08T22:17:30.270Z,
//   2060-07-08T22:17:30.270Z
// ]

Operators

Operators are the functions, which could be used to filtering, mapping or reducing ranges

Filter Operators (Predicates)

  • isLeapYear -- returns true if the year of the transmitted date is a leap year.
  • hasWeekday -- returns true if the day of the week of the transmitted date coincides with the specified days.
  • keepUnique -- returns true if the value is unique in the current state.

Examples

const { NumberRange, keepUnique } = require('ranging');

const keepUniqueWithState = keepUnique();

console.log(
  new NumberRange(0, 10)
    .map((el) => el % 2)
    .filter(keepUniqueWithState)
    .collect(),
);
// [0, 1]
const { DayRangem, hasWeekday } = require('ranging');

console.log(
  new DayRange(new Date('2023-07-08'))
    .filter(hasWeekday(0, 6)) // Get only Sunday and Saturday
    .take(4)
    .collect(),
);

// [
//   2023-07-08T00:00:00.000Z,
//   2023-07-09T00:00:00.000Z,
//   2023-07-15T00:00:00.000Z,
//   2023-07-16T00:00:00.000Z
// ]

Mapper Operators (Functors)

No mapper operators yet...

Reducer Operators

  • sum -- calculate safety sum of numbers (slower than a + b)

Merging ranges

Glue ranges together.

const { NumberRange, WalkerRange, MergeRange } = require('ranging');

const numbers = new NumberRange(undefined, 5);
const walker = new WalkerRange('hello');

const merging = new MergeRange((_) => _, [numbers, walker]);

console.log(...merging);
// 0 1 2 3 4 5 h e l l o

You can use rule function to specify how ranges will be merging:

const { NumberRange, WalkerRange, MergeRange } = require('ranging');

const numbers = new NumberRange(undefined, 5);
const walker = new WalkerRange('hello');

const merging = new MergeRange(
  (_, index, { switchTo }) => switchTo(index % 2),
  [numbers, walker]
);

console.log(...merging);
// 0 1 h 2 e 3 l 4 l 5 o

Zipping ranges

Works as the known function from other programming languages.

const { NumberRange, WalkerRange, ZipRange } = require('ranging');

const numbers = new NumberRange(1);
const chars = new WalkerRange('Hello');

const zipped = new ZipRange(chars, numbers);

console.log(...zipped);
// { H: 1 } { e: 2 } { l: 3 } { l: 4 } { o: 5 }

Note: numbers is set up as an infinite iterator, but zipping it with chars limits it to just 5 entries.

Combining Ranges

const { NumberRange, WalkerRange } = require('ranging');

console.log(
  new NumberRange()
    .combine(new WalkerRange('hello'))
    .collect(),
);
// [ [ 0, 'h' ], [ 1, 'e' ], [ 2, 'l' ], [ 3, 'l' ], [ 4, 'o' ] ]

License

Copyright © 2021 by Kirill (Crinax), Eugene Gritz (maycircle). MIT license.

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 4.1.0
    9
    • latest

Version History

Package Sidebar

Install

npm i ranging

Weekly Downloads

19

Version

4.1.0

License

MIT

Unpacked Size

155 kB

Total Files

60

Last publish

Collaborators

  • crinax