A small library which parses and manipulates comma-delimited integer ranges (such as
1-3,8-10), which are typically used in print dialogs to indicate which pages to print.
- Addition (e.g.,
- Subtraction (e.g.,
- Inclusion check (e.g.,
- Intersection (e.g.,
- Unbounded ranges (e.g.,
5-to mean "all integers >= 5") (optional)
- Ranges including negative integers or zero
- ES6 iterator (
for ... of, spread operator)
- Array creation ("flatten")
Internal data are always sorted and normalized to the smallest possible representation.
Install via npm or yarn:
npm install multi-integer-range
This library has no external dependencies, and can be used with module bundlers such as Webpack.
;// OR:// const MultiRange = require('multi-integer-range').MultiRange;// const MultiRange = require('multi-integer-range').default;const pages = '1-5,12-15';pages;console; // '1,3-15'console; // trueconsole; // [1, 3, 4, 5, ... , 15]console; // [[1, 1], [3, 15]]console; // 2
Some methods and the constructor take one Initializer parameter. An initializer is one of the following:
- a valid string (e.g.,
- an array of integers (e.g.,
[1, 2, 3, 5])
- an array of
[min, max]tuples (e.g.,
[[1, 3], [5, 5]])
- mixture of integers and tuples (e.g.,
[[1, 3], 5])
- a single integer (e.g.,
- another MultiRange instance
Pass it to the constructor to create a MultiRange object,
or pass nothing to create an empty MultiRange object.
A shorthand constructor function
multirange() is also available,
which you can use according to your preference.
;const mr1 = 7 2 9 1 8 3;const mr2 = '1-2, 3, 7-9';const mr3 = 1 3 7 9;const mr4 = mr1; // cloneconst mr5 = ;
These five instances (
mr5) hold identical range data
because internal data are always normalized.
The string parser is permissive and accepts space characters before/after comma/hyphens. Order is not important either, and overlapped numbers are silently ignored.
const mr = '3,\t8-3,2,3,\n10, 9 - 7 ';console; // prints '2-10'
Manipulation methods are mutable and chainable by design.
That is, for example, when you call
append(5), it will change
the internal representation and return the modified self,
rather than returning a new instance.
To get a copy of the instance, use
clone(), or alternatively the copy constructor (
var copy = new MultiRange(orig)).
new MultiRange(data?: Initializer, options?)Creates a new MultiRange object. The
optionsobject modifies the parsing behavior (see below).
clone(): MultiRangeClones this instance.
append(value: Initializer): MultiRangeAppends
valueto this instance.
subtract(value: Initializer): MultiRangeSubtracts
valuefrom this instance.
intersect(value: Initializer): MultiRangeRemoves integers which are not included in
has(value: Initializer): booleanChecks if the instance contains
length(): numberCalculates how many numbers are effectively included in this instance (ie, 5 for '3,5-7,9'). Returns Inifnity for an unbounded range.
segmentLength(): numberReturns the number of range segments (ie, 3 for '3,5-7,9' and 0 for an empty range)
equals(cmp: Initializer): booleanChecks if two MultiRange data are identical.
isUnbounded(): booleanReturns if the instance is unbounded.
min(): number | undefinedReturns the minimum integer. May return -Infinity.
max(): number | undefinedReturns the maxinum integer. May return Infinity.
shift(): number | undefinedRemoves the minimum integer and returns it.
pop(): number | undefinedRemoves the maxinum integer and returns it.
toString(): stringReturns the string respresentation of this MultiRange.
getRanges(): [number, number]Exports the whole range data as an array of [number, number] tuples.
toArray(): numberBuilds an array of integer which holds all integers in this MultiRange. This may be slow and memory-consuming for large ranges such as '1-10000'.
getIterator(): ObjectReturns an ES6-compatible iterator. See the description below.
options that can be passed to the constructor:
parseNegative(boolean, default = false): Enables parsing negative ranges (e.g.,
parseUnbounded(boolean, default = false): Enables parsing unbounded ranges (e.g.,
Unbounded Ranges (optional)
You can use unbounded (aka infinite) ranges.
Parsing unbounded ranges is disabled by default,
and you have to enable it via the
parseUnbounded option parameter.
Note that the
parseUnbounded option only affects the way string initializers are parsed.
You do not have to pass any option to create unbounded ranges using non-string initializers.
parseUnbounded is enabled at the constructor,
subsequent chained methods will also correctly parse unbounded ranges.
The manipulation methods work just as expected with unbounded ranges:
const unbounded = ;// Chained methods recognize unbounded ranges, tooconsole; // '0,5-'console; // '-2,6,8-10'console; // true// Intersection is especially useful to "trim" any unbounded ranges:const userInput = '-10,15-20,90-';const pagesInMyDoc = 1 100; // '1-100'const pagesToPrint = ;console; // prints '1-10,15-20,90-100'
Unbounded ranges cannot be iterated over, and you cannot call
toArray() for obvious reasons.
length() for unbounded ranges will return
Ranges Containing Zero and Negative Integers
You can safely handle ranges containing zero and negative integers, including
The syntax for denoting negative integers in a string initializer is a bit tricky, though;
you need to always contain all negative integers in parentheses.
You also need to pass
parseNegative option to make the parser recognize
negative integers contained in parentheses.
const mr1 = '(-5),(-1)-0' parseNegative: true ; // -5, -1 and 0mr1; // -4 to -2console; // prints '(-5)-0'
Note that the
parseNegative option only affects the way string initializers are parsed.
You do not have to pass any option to create negative ranges using non-string initializers.
parseNegative is enabled at the constructor,
subsequent chained methods will also recognize and parse negative ranges.
const mr2 = ;console; // prints '(-5),(-3)'
ES2015 (ES6) iterator: If
Symbol.iterator is defined
(either natively or by a polyfill), you can simply iterate over the instance like this:
for const page ofconsole;// prints 2, 5, 6 and 7// Instead of calling toArray() ...const arr = ...; // array spreading// arr becomes [2, 5, 6, 7]
Symbol.iterator is not available, you can still access the iterator
implementation and use it manually like this:
var it =page;while !page = itnextdoneconsole;
TypeScript Definition File
A TypeScript definition file (
*.d.ts) is included in this package.
The definition file only contains declarations that are compatible with ES5.
If your TypeScript project needs support for iterators (e.g.,
for ... of or
add the following snippet somewhere in your project to avoid compile-time errors.
In addition, if your project is
--target es5, you'll need a polyfill for symbols,
--downlevelIteration compile option (available since TS 2.3),
--lib es2015.iterable compile option.
If these bother you, you can always manually use
getIterator() as described above.
Performance Considerations: This library works efficiently for large ranges
as long as they're mostly continuous (e.g.,
However, this library is not intended to be efficient
with a heavily fragmentated set of integers which are scarcely continuous
(e.g., random 10000 integers between 1 to 1000000).
No Integer Type Checks: Make sure you are not passing floating-point
to this library. For example, don't do
For performance reasons, the library does not check if a passed number is an integer.
Passing a float will result in an unexpected and unrecoverable behavior.
Building and Testing
npm install npm run build npm test
Please report any bugs and suggestions using GitHub issues.
Soichiro Miki (https://github.com/smikitky)