Socket
Socket
Sign inDemoInstall

to-regex-range

Package Overview
Dependencies
4
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.2 to 2.0.0

183

index.js
/*!
* to-regex-range <https://github.com/jonschlinkert/to-regex-range>
*
* Copyright (c) 2015, Jon Schlinkert.
* Licensed under the MIT License.
* Copyright (c) 2015, 2017, Jon Schlinkert.
* Released under the MIT License.
*/

@@ -12,5 +12,5 @@

var isNumber = require('is-number');
var cache = {range: {}, rangeToPattern: {}};
var cache = {};
function toRegexRange(min, max) {
function toRegexRange(min, max, options) {
if (isNumber(min) === false) {

@@ -20,4 +20,4 @@ throw new RangeError('toRegexRange: first argument is invalid.');

if (typeof max === 'undefined') {
return '' + min;
if (typeof max === 'undefined' || min === max) {
return String(min);
}

@@ -29,36 +29,57 @@

options = options || {};
var key = min + ':' + max + '=' + options.capture;
if (cache.hasOwnProperty(key)) {
return cache[key].result;
}
var a = Math.min(min, max);
var b = Math.max(min, max);
if (a === b) return String(a);
var key = min + ':' + max;
if (cache.range.hasOwnProperty(key)) {
return cache.range[key];
if (Math.abs(a - b) === 1) {
var result = min + '|' + max;
if (options.capture) {
return '(' + result + ')';
}
return result;
}
var isPadded = padding(min) || padding(max);
var positives = [];
var negatives = [];
var tok = {min: min, max: max, a: a, b: b};
if (isPadded) {
tok.isPadded = isPadded;
tok.maxLen = String(tok.max).length;
}
if (a < 0) {
var newMin = b < 0 ? Math.abs(b) : 1;
var newMax = Math.abs(a);
negatives = splitToPatterns(newMin, newMax);
a = 0;
negatives = splitToPatterns(newMin, newMax, tok);
a = tok.a = 0;
}
if (b >= 0) {
positives = splitToPatterns(a, b);
positives = splitToPatterns(a, b, tok);
}
var str = siftPatterns(negatives, positives);
cache.range[key] = str;
return str;
tok.negatives = negatives;
tok.positives = positives;
tok.result = siftPatterns(negatives, positives);
if (options.capture && (positives.length + negatives.length) > 1) {
tok.result = '(' + tok.result + ')';
}
cache[key] = tok;
return tok.result;
}
function siftPatterns(negatives, positives) {
var onlyNegative = filterPatterns(negatives, positives, '-');
var onlyPositive = filterPatterns(positives, negatives, '');
var intersected = filterPatterns(negatives, positives, '-?', true);
var subpatterns = onlyNegative.concat(intersected || []).concat(onlyPositive || []);
var onlyNegative = filterPatterns(negatives, positives, '-') || [];
var onlyPositive = filterPatterns(positives, negatives, '') || [];
var intersected = filterPatterns(negatives, positives, '-?', true) || [];
var subpatterns = onlyNegative.concat(intersected).concat(onlyPositive);
return subpatterns.join('|');

@@ -106,7 +127,2 @@ }

var key = start + ':' + stop;
if (cache.rangeToPattern.hasOwnProperty(key)) {
return cache.rangeToPattern[key];
}
var zipped = zip(String(start), String(stop));

@@ -119,5 +135,5 @@ var len = zipped.length, i = -1;

while (++i < len) {
var range = zipped[i];
var startDigit = range[0];
var stopDigit = range[1];
var numbers = zipped[i];
var startDigit = numbers[0];
var stopDigit = numbers[1];

@@ -128,3 +144,3 @@ if (startDigit === stopDigit) {

} else if (startDigit !== '0' || stopDigit !== '9') {
pattern += toRange(startDigit, stopDigit);
pattern += toCharacterClass(startDigit, stopDigit);

@@ -143,13 +159,3 @@ } else {

/**
* Zip strings (`for in` can be used on string characters)
*/
function zip(a, b) {
var arr = [];
for (var ch in a) arr.push([a[ch], b[ch]]);
return arr;
}
function splitToPatterns(min, max) {
function splitToPatterns(min, max, tok) {
var ranges = splitToRanges(min, max);

@@ -159,4 +165,4 @@ var len = ranges.length;

var tokens = [];
var start = min;
var tokens = [];
var prev;

@@ -166,9 +172,10 @@

var range = ranges[idx];
var tok = rangeToPattern(start, range);
var obj = rangeToPattern(start, range);
var zeros = '';
if (prev && prev.pattern === tok.pattern) {
if (!tok.isPadded && prev && prev.pattern === obj.pattern) {
if (prev.digits.length > 1) {
prev.digits.pop();
}
prev.digits.push(tok.digits[0]);
prev.digits.push(obj.digits[0]);
prev.string = prev.pattern + toQuantifier(prev.digits);

@@ -179,6 +186,10 @@ start = range + 1;

tok.string = tok.pattern + toQuantifier(tok.digits);
tokens.push(tok);
if (tok.isPadded) {
zeros = padZeros(range, tok);
}
obj.string = zeros + obj.pattern + toQuantifier(obj.digits);
tokens.push(obj);
start = range + 1;
prev = tok;
prev = obj;
}

@@ -190,28 +201,55 @@

function filterPatterns(arr, comparison, prefix, intersection) {
var len = arr.length, i = -1;
var intersected = [];
var res = [];
comparison = comparison.map(function(tok) {
return tok.string;
});
while (++i < len) {
for (var i = 0; i < arr.length; i++) {
var tok = arr[i];
var ele = tok.string;
if (!intersection && comparison.indexOf(ele) === -1) {
if (prefix === '-' && ele.charAt(0) === '0') {
ele = '0*' + ele.slice(ele.charAt(1) === '{' ? 4 : 1);
}
if (!intersection && !contains(comparison, 'string', ele)) {
res.push(prefix + ele);
}
if (intersection && comparison.indexOf(ele) !== -1) {
intersected.push(prefix + ele);
if (intersection && contains(comparison, 'string', ele)) {
res.push(prefix + ele);
}
}
return intersection ? intersected : res;
return res;
}
function countNines(num, len) {
return String(num).slice(0, -len) + repeat('9', len);
/**
* Zip strings (`for in` can be used on string characters)
*/
function zip(a, b) {
var arr = [];
for (var ch in a) arr.push([a[ch], b[ch]]);
return arr;
}
function compare(a, b) {
return a > b ? 1 : b > a ? -1 : 0;
}
function push(arr, ele) {
if (arr.indexOf(ele) === -1) arr.push(ele);
return arr;
}
function contains(arr, key, val) {
for (var i = 0; i < arr.length; i++) {
if (arr[i][key] === val) {
return true;
}
}
return false;
}
function countNines(min, len) {
return String(min).slice(0, -len) + repeat('9', len);
}
function countZeros(integer, zeros) {

@@ -230,13 +268,24 @@ return integer - (integer % Math.pow(10, zeros));

function toRange(a, b) {
function toCharacterClass(a, b) {
return '[' + a + '-' + b + ']';
}
function compare(a, b) {
return a > b ? 1 : b > a ? -1 : 0;
function padding(str) {
return /^-?(0+)\d/.exec(str);
}
function push(arr, ele) {
if (arr.indexOf(ele) === -1) arr.push(ele);
return arr;
function padZeros(val, tok) {
if (tok.isPadded) {
var diff = Math.abs(tok.maxLen - String(val).length);
switch (diff) {
case 0:
return '';
case 1:
return '0';
default: {
return '0{' + diff + '}';
}
}
}
return val;
}

@@ -243,0 +292,0 @@

{
"name": "to-regex-range",
"description": "Returns a regex-compatible range from two numbers, min and max. Validated against more than 1.1 million generated unit tests that run in less than 400ms! Useful for creating regular expressions to validate numbers, ranges, years, etc.",
"version": "1.0.2",
"description": "Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.87 million test assertions.",
"version": "2.0.0",
"homepage": "https://github.com/jonschlinkert/to-regex-range",

@@ -24,7 +24,10 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)",

"is-number": "^3.0.0",
"repeat-string": "^1.5.4"
"repeat-string": "^1.6.1"
},
"devDependencies": {
"gulp-format-md": "^0.1.11",
"mocha": "^3.1.2"
"fill-range": "^3.1.1",
"gulp-format-md": "^0.1.12",
"mocha": "^3.2.0",
"text-table": "^0.2.0",
"time-diff": "^0.3.1"
},

@@ -74,6 +77,22 @@ "keywords": [

},
"helpers": [
"./examples.js"
],
"reflinks": [
"verb"
"0-5",
"0-9",
"1-5",
"1-9",
"expand-range",
"fill-range",
"micromatch",
"npm",
"range-regex",
"repeat-element",
"repeat-string",
"verb",
"verb-generate-readme",
"yarn"
]
}
}
# to-regex-range [![NPM version](https://img.shields.io/npm/v/to-regex-range.svg?style=flat)](https://www.npmjs.com/package/to-regex-range) [![NPM monthly downloads](https://img.shields.io/npm/dm/to-regex-range.svg?style=flat)](https://npmjs.org/package/to-regex-range) [![NPM total downloads](https://img.shields.io/npm/dt/to-regex-range.svg?style=flat)](https://npmjs.org/package/to-regex-range) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/to-regex-range.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/to-regex-range)
> Returns a regex-compatible range from two numbers, min and max. Validated against more than 1.1 million generated unit tests that run in less than 400ms! Useful for creating regular expressions to validate numbers, ranges, years, etc.
> Pass two numbers, get a regex-compatible source string for matching ranges. Validated against more than 2.87 million test assertions.

@@ -13,37 +13,119 @@ ## Install

## Notes
Install with [yarn](https://yarnpkg.com):
Validated against [1,117,543 generated unit tests](./test/test.js), to provide brute-force verification that the generated regex-ranges are correct.
```sh
$ yarn add to-regex-range
```
<details>
<summary><strong>What does this do?</strong></summary>
<br>
This libary generates the `source` string to be passed to `new RegExp()` for matching a range of numbers.
**Example**
```js
var toRegexRange = require('to-regex-range');
var regex = new RegExp(toRegexRange('15', '95'));
```
A string is returned so that you can do whatever you need with it before passing it to `new RegExp()` (like adding `^` or `$` boundaries, defining flags, or combining it another string).
<br>
</details>
<details>
<summary><strong>Why use this library?</strong></summary>
<br>
### Convenience
Creating regular expressions for matching numbers gets deceptively complicated pretty fast.
For example, let's say you need a validation regex for matching part of a user-id, postal code, social security number, tax id, etc:
* regex for matching `1` => `/1/` (easy enough)
* regex for matching `1` through `5` => `/[1-5]/` (not bad...)
* regex for matching `1` or `5` => `/(1|5)/` (still easy...)
* regex for matching `1` through `50` => `/([1-9]|[1-4][0-9]|50)/` (uh-oh...)
* regex for matching `1` through `55` => `/([1-9]|[1-4][0-9]|5[0-5])/` (no prob, I can do this...)
* regex for matching `1` through `555` => `/([1-9]|[1-9][0-9]|[1-4][0-9]{2}|5[0-4][0-9]|55[0-5])/` (maybe not...)
* regex for matching `0001` through `5555` => `/(0{3}[1-9]|0{2}[1-9][0-9]|0[1-9][0-9]{2}|[1-4][0-9]{3}|5[0-4][0-9]{2}|55[0-4][0-9]|555[0-5])/` (okay, I get the point!)
The numbers are contrived, but they're also really basic. In the real world you might need to generate a regex on-the-fly for validation.
**Learn more**
If you're interested in learning more about [character classes](http://www.regular-expressions.info/charclass.html) and other regex features, I personally have always found [regular-expressions.info](http://www.regular-expressions.info/charclass.html) to be pretty useful.
### Heavily tested
As of April 22, 2017, this library runs [2,783,483 test assertions](./test/test.js) against generated regex-ranges to provide brute-force verification that results are indeed correct.
Tests run in ~870ms on my MacBook Pro, 2.5 GHz Intel Core i7.
### Highly optimized
Generated regular expressions are highly optimized:
* duplicate sequences and character classes are reduced using quantifiers
* smart enough to use `?` conditionals when number(s) or range(s) can be positive or negative
* uses fragment caching to avoid processing the same exact string more than once
<br>
</details>
## Usage
Add this library to your javascript application with the following line of code
```js
var toRegexRange = require('to-regex-range');
var re = new RegExp(toRegexRange('1', '99'));
re.test('50');
//=> true
```
**Examples**
The main export is a function that takes two integers: the `min` value and `max` value (formatted as strings or numbers).
```js
console.log(toRegexRange('111', '555'));
//=> 11[1-9]|1[2-9][0-9]|[2-4][0-9]{2}|5[0-4][0-9]|55[0-5]
var source = toRegexRange('15', '95');
//=> 1[5-9]|[2-8][0-9]|9[0-5]
console.log(toRegexRange('5', '5'));
//=> 5
var re = new RegExp('^' + source + '$');
console.log(re.test('14')); //=> false
console.log(re.test('50')); //=> true
console.log(re.test('94')); //=> true
console.log(re.test('96')); //=> false
```
console.log(toRegexRange('5', '6'));
//=> [5-6]
## Examples
console.log(toRegexRange('51', '229'));
//=> 5[1-9]|[6-9][0-9]|1[0-9]{2}|2[0-2][0-9]
| **Range** | **Result** | **Compile time** |
| --- | --- | --- |
| `toRegexRange('5, 5')` | `5` | _26μs_ |
| `toRegexRange('5, 6')` | `5\|6` | _47μs_ |
| `toRegexRange('29, 51')` | `29\|[3-4][0-9]\|5[0-1]` | _452μs_ |
| `toRegexRange('31, 877')` | `3[1-9]\|[4-9][0-9]\|[1-7][0-9]{2}\|8[0-6][0-9]\|87[0-7]` | _756μs_ |
| `toRegexRange('111, 555')` | `11[1-9]\|1[2-9][0-9]\|[2-4][0-9]{2}\|5[0-4][0-9]\|55[0-5]` | _61μs_ |
| `toRegexRange('-10, 10')` | `-[1-9]\|-?10\|[0-9]` | _69μs_ |
| `toRegexRange('-100, -10')` | `-1[0-9]\|-[2-9][0-9]\|-100` | _42μs_ |
| `toRegexRange('-100, 100')` | `-[1-9]\|-?[1-9][0-9]\|-?100\|[0-9]` | _42μs_ |
| `toRegexRange('001, 100')` | `0{2}[1-9]\|0[1-9][0-9]\|100` | _135μs_ |
| `toRegexRange('0010, 1000')` | `0{2}1[0-9]\|0{2}[2-9][0-9]\|0[1-9][0-9]{2}\|1000` | _53μs_ |
| `toRegexRange('1, 2')` | `1\|2` | _18μs_ |
| `toRegexRange('1, 5')` | `[1-5]` | _23μs_ |
| `toRegexRange('1, 10')` | `[1-9]\|10` | _22μs_ |
| `toRegexRange('1, 100')` | `[1-9]\|[1-9][0-9]\|100` | _23μs_ |
| `toRegexRange('1, 1000')` | `[1-9]\|[1-9][0-9]{1,2}\|1000` | _60μs_ |
| `toRegexRange('1, 10000')` | `[1-9]\|[1-9][0-9]{1,3}\|10000` | _117μs_ |
| `toRegexRange('1, 100000')` | `[1-9]\|[1-9][0-9]{1,4}\|100000` | _42μs_ |
| `toRegexRange('1, 1000000')` | `[1-9]\|[1-9][0-9]{1,5}\|1000000` | _46μs_ |
| `toRegexRange('1, 10000000')` | `[1-9]\|[1-9][0-9]{1,6}\|10000000` | _60μs_ |
console.log(toRegexRange('29', '51'));
//=> 29|[3-4][0-9]|5[0-1]
## Heads up!
console.log(toRegexRange('1', '100000'));
//=> [1-9]|[1-9][0-9]{1,4}|100000
```
**Order of arguments**

@@ -54,56 +136,32 @@ When the `min` is larger than the `max`, values will be flipped to create a valid range:

toRegexRange('51', '29');
```
Is effectively flipped to:
```js
toRegexRange('29', '51');
//=> 29|[3-4][0-9]|5[0-1]
```
**Heads up!**
**Steps / increments**
This library does not support steps (increments) or zero-padding.
This library does not support steps (increments). A pr to add support would be welcome.
## History
### v1.0
### v2.0.0 - 2017-04-21
More optimizations! As of v1.0, repeating ranges are now grouped using quantifiers. Processing time is roughly the same, but the generated regex is much smaller, which should result in faster matching.
**New features**
**Key**
Adds support for zero-padding!
_(for the before/after comparison tables)_
### v1.0.0
* `range`: the generated range, e.g. `toRegexRange(1, 10000000)`
* `stats`: size of the generated string, and processing time
* `result`: generated string
**Optimizations**
#### Before
Repeating ranges are now grouped using quantifiers. rocessing time is roughly the same, but the generated regex is much smaller, which should result in faster matching.
Patterns generated before v1.0 changes:
**Range** | **Stats** | **Result**
--- | --- | ---
`1..10000000` | `99 B` (11ms 666μs) | `([1-9]|[1-9][0-9]|[1-9][0-9]{2}|[1-9][0-9]{3}|[1-9][0-9]{4}|[1-9][0-9]{5}|[1-9][0-9]{6}|10000000)`
`1..1000000` | `84 B` (2ms 96μs) | `([1-9]|[1-9][0-9]|[1-9][0-9]{2}|[1-9][0-9]{3}|[1-9][0-9]{4}|[1-9][0-9]{5}|1000000)`
`1..100000` | `69 B` (1ms 674μs) | `([1-9]|[1-9][0-9]|[1-9][0-9]{2}|[1-9][0-9]{3}|[1-9][0-9]{4}|100000)`
`1..10000` | `54 B` (2ms 40μs) | `([1-9]|[1-9][0-9]|[1-9][0-9]{2}|[1-9][0-9]{3}|10000)`
`1..1000` | `39 B` (1ms 263μs) | `([1-9]|[1-9][0-9]|[1-9][0-9]{2}|1000)`
`1..100` | `24 B` (1ms 905μs) | `([1-9]|[1-9][0-9]|100)`
`1..10` | `12 B` (383μs) | `([1-9]|10)`
`1..3` | `9 B` (260μs) | `([1-3])`
#### After
With v1.0 optimizations.
**Range** | **Stats** | **Result**
--- | --- | ---
`1..10000000` | `34 B` (11ms 702μs) | `([1-9]|[1-9][0-9]{1,6}|10000000)`
`1..1000000` | `33 B` (1ms 274μs) | `([1-9]|[1-9][0-9]{1,5}|1000000)`
`1..100000` | `32 B` (726μs) | `([1-9]|[1-9][0-9]{1,4}|100000)`
`1..10000` | `31 B` (2ms 432μs) | `([1-9]|[1-9][0-9]{1,3}|10000)`
`1..1000` | `30 B` (507μs) | `([1-9]|[1-9][0-9]{1,2}|1000)`
`1..100` | `24 B` (267μs) | `([1-9]|[1-9][0-9]|100)`
`1..10` | `12 B` (240μs) | `([1-9]|10)`
`1..3` | `9 B` (665μs) | `([1-3])`
## Attribution
Inspired by the python lib [range-regex](https://github.com/dimka665/range-regex).
Inspired by the python library [range-regex](https://github.com/dimka665/range-regex).

@@ -126,8 +184,8 @@ ## About

_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
To generate the readme, run the following command:
```sh
$ npm install -g verb verb-generate-readme && verb
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```

@@ -137,6 +195,6 @@

Install dev dependencies:
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install -d && npm test
$ npm install && npm test
```

@@ -149,11 +207,11 @@

* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
### License
Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT license](https://github.com/jonschlinkert/to-regex-range/blob/master/LICENSE).
Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.2.0, on October 19, 2016._
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.5.0, on April 22, 2017._

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc