Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

string-natural-compare

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

string-natural-compare - npm Package Compare versions

Comparing version 2.0.3 to 3.0.0

124

natural-compare.js
'use strict';
var alphabet;
var alphabetIndexMap;
var alphabetIndexMapLength = 0;
const defaultAlphabetIndexMap = [];
function isNumberCode(code) {
return code >= 48 && code <= 57;
return code >= 48/* '0' */ && code <= 57/* '9' */;
}
function naturalCompare(a, b) {
var lengthA = (a += '').length;
var lengthB = (b += '').length;
var aIndex = 0;
var bIndex = 0;
function naturalCompare(a, b, opts) {
if (typeof a !== 'string') {
throw new TypeError(`The first argument must be a string. Received type '${typeof a}'`);
}
if (typeof b !== 'string') {
throw new TypeError(`The second argument must be a string. Received type '${typeof b}'`);
}
while (aIndex < lengthA && bIndex < lengthB) {
var charCodeA = a.charCodeAt(aIndex);
var charCodeB = b.charCodeAt(bIndex);
const lengthA = a.length;
const lengthB = b.length;
let indexA = 0;
let indexB = 0;
let alphabetIndexMap = defaultAlphabetIndexMap;
if (opts) {
if (opts.caseInsensitive) {
a = a.toLowerCase();
b = b.toLowerCase();
}
if (opts.alphabet) {
alphabetIndexMap = buildAlphabetIndexMap(opts.alphabet);
}
}
while (indexA < lengthA && indexB < lengthB) {
let charCodeA = a.charCodeAt(indexA);
let charCodeB = b.charCodeAt(indexB);
if (isNumberCode(charCodeA)) {

@@ -26,14 +43,14 @@ if (!isNumberCode(charCodeB)) {

var numStartA = aIndex;
var numStartB = bIndex;
let numStartA = indexA;
let numStartB = indexB;
while (charCodeA === 48 && ++numStartA < lengthA) {
while (charCodeA === 48/* '0' */ && ++numStartA < lengthA) {
charCodeA = a.charCodeAt(numStartA);
}
while (charCodeB === 48 && ++numStartB < lengthB) {
while (charCodeB === 48/* '0' */ && ++numStartB < lengthB) {
charCodeB = b.charCodeAt(numStartB);
}
var numEndA = numStartA;
var numEndB = numStartB;
let numEndA = numStartA;
let numEndB = numStartB;

@@ -47,4 +64,4 @@ while (numEndA < lengthA && isNumberCode(a.charCodeAt(numEndA))) {

var difference = numEndA - numStartA - numEndB + numStartB; // numA length - numB length
if (difference) {
let difference = numEndA - numStartA - numEndB + numStartB; // numA length - numB length
if (difference !== 0) {
return difference;

@@ -55,3 +72,3 @@ }

difference = a.charCodeAt(numStartA++) - b.charCodeAt(numStartB++);
if (difference) {
if (difference !== 0) {
return difference;

@@ -61,4 +78,4 @@ }

aIndex = numEndA;
bIndex = numEndB;
indexA = numEndA;
indexB = numEndB;
continue;

@@ -69,4 +86,4 @@ }

if (
charCodeA < alphabetIndexMapLength &&
charCodeB < alphabetIndexMapLength &&
charCodeA < alphabetIndexMap.length &&
charCodeB < alphabetIndexMap.length &&
alphabetIndexMap[charCodeA] !== -1 &&

@@ -81,12 +98,12 @@ alphabetIndexMap[charCodeB] !== -1

++aIndex;
++bIndex;
++indexA;
++indexB;
}
if (aIndex >= lengthA && bIndex < lengthB && lengthA >= lengthB) {
return -1;
if (indexA < lengthA) { // `b` is a substring of `a`
return 1;
}
if (bIndex >= lengthB && aIndex < lengthA && lengthB >= lengthA) {
return 1;
if (indexB < lengthB) { // `a` is a substring of `b`
return -1;
}

@@ -97,35 +114,28 @@

naturalCompare.caseInsensitive = naturalCompare.i = function(a, b) {
return naturalCompare(('' + a).toLowerCase(), ('' + b).toLowerCase());
};
const alphabetIndexMapCache = {};
Object.defineProperties(naturalCompare, {
alphabet: {
get: function() {
return alphabet;
},
function buildAlphabetIndexMap(alphabet) {
const existingMap = alphabetIndexMapCache[alphabet];
if (existingMap !== undefined) {
return existingMap;
}
set: function(value) {
alphabet = value;
alphabetIndexMap = [];
const indexMap = [];
const maxCharCode = alphabet.split('').reduce((maxCode, char) => {
return Math.max(maxCode, char.charCodeAt(0));
}, 0);
var i = 0;
for (let i = 0; i <= maxCharCode; i++) {
indexMap.push(-1);
}
if (alphabet) {
for (; i < alphabet.length; i++) {
alphabetIndexMap[alphabet.charCodeAt(i)] = i;
}
}
for (let i = 0; i < alphabet.length; i++) {
indexMap[alphabet.charCodeAt(i)] = i;
}
alphabetIndexMapLength = alphabetIndexMap.length;
alphabetIndexMapCache[alphabet] = indexMap;
for (i = 0; i < alphabetIndexMapLength; i++) {
if (alphabetIndexMap[i] === undefined) {
alphabetIndexMap[i] = -1;
}
}
},
},
});
return indexMap;
}
module.exports = naturalCompare;
{
"name": "string-natural-compare",
"version": "2.0.3",
"version": "3.0.0",
"description": "Compare alphanumeric strings the same way a human would, using a natural order algorithm",

@@ -11,24 +11,5 @@ "author": "Nathan Woltman <nwoltman@outlook.com>",

],
"repository": {
"type": "git",
"url": "https://github.com/nwoltman/string-natural-compare"
},
"bugs": {
"url": "https://github.com/nwoltman/string-natural-compare/issues"
},
"repository": "github:nwoltman/string-natural-compare",
"homepage": "https://github.com/nwoltman/string-natural-compare",
"devDependencies": {
"coveralls": "^3.0.2",
"grunt": "~1.0.3",
"grunt-eslint": "^21.0.0",
"grunt-mocha-istanbul": "^5.0.2",
"grunt-mocha-test": "^0.13.3",
"istanbul": "^0.4.5",
"jit-grunt": "^0.10.0",
"mocha": "^5.2.0",
"should": "^13.2.1"
},
"scripts": {
"test": "grunt"
},
"bugs": "https://github.com/nwoltman/string-natural-compare/issues",
"keywords": [

@@ -46,3 +27,30 @@ "string",

"alphanumeric"
]
],
"eslintIgnore": [
"benchmark/node_modules/",
"coverage/"
],
"nyc": {
"reporter": [
"html",
"text-summary"
],
"check-coverage": true,
"branches": 100,
"lines": 100,
"statements": 100
},
"devDependencies": {
"@nwoltman/eslint-config": "^0.5.0",
"coveralls": "^3.0.7",
"eslint": "^6.6.0",
"mocha": "^6.2.2",
"nyc": "^14.1.1",
"should": "^13.2.3"
},
"scripts": {
"lint": "eslint .",
"test": "eslint . && nyc mocha",
"coveralls": "nyc report --reporter=text-lcov | coveralls"
}
}

@@ -8,3 +8,3 @@ # String Natural Compare

[![Coverage Status](https://coveralls.io/repos/nwoltman/string-natural-compare/badge.svg?branch=master)](https://coveralls.io/r/nwoltman/string-natural-compare?branch=master)
[![devDependencies Status](https://david-dm.org/nwoltman/string-natural-compare/dev-status.svg)](https://david-dm.org/nwoltman/string-natural-compare?type=dev)
[![Dependencies Status](https://img.shields.io/david/nwoltman/string-natural-compare)](https://david-dm.org/nwoltman/string-natural-compare)

@@ -19,13 +19,8 @@ ```

This module provides two functions:
This module exports a function that returns a number indicating whether one string should come before, after, or is the same as another string.
It can be used directly with the native [`.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) array method.
+ `naturalCompare`
+ `naturalCompare.caseInsensitive` (alias: `naturalCompare.i`)
These functions return a number indicating whether one string should come before, after, or is the same as another string.
They can be easily used with the native [`.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) array method.
### Fast and Robust
This module uses a performant and robust algorithm to compare alphanumeric strings. It can compare strings containing any size of number and is heavily tested with a custom [benchmark suite](https://github.com/nwoltman/string-natural-compare/tree/master/benchmark) to make sure that it is as fast as possible, even when a [custom alphabet](#custom-alphabet) has been configured.
This module can compare strings containing any size of number and is heavily tested with a custom [benchmark suite](https://github.com/nwoltman/string-natural-compare/tree/master/benchmark) to make sure that it is as fast as possible.

@@ -36,6 +31,4 @@

```sh
# npm
npm install string-natural-compare --save
# yarn
# or
yarn add string-natural-compare

@@ -47,19 +40,28 @@ ```

#### `naturalCompare(strA, strB[, options])`
+ `strA` (_string_)
+ `strB` (_string_)
+ `options` (_object_) - Optional options object with the following options:
+ `caseInsensitive` (_boolean_) - Set to `true` to compare strings case-insensitively. Default: `false`.
+ `alphabet` (_string_) - A string of characters that define a custom character ordering. Default: `undefined`.
```js
var naturalCompare = require('string-natural-compare');
const naturalCompare = require('string-natural-compare');
// Simple case-sensitive sorting
var a = ['z1.doc', 'z10.doc', 'z17.doc', 'z2.doc', 'z23.doc', 'z3.doc'];
a.sort(naturalCompare);
// Simple, case-sensitive sorting
const files = ['z1.doc', 'z10.doc', 'z17.doc', 'z2.doc', 'z23.doc', 'z3.doc'];
files.sort(naturalCompare);
// -> ['z1.doc', 'z2.doc', 'z3.doc', 'z10.doc', 'z17.doc', 'z23.doc']
// Simple case-insensitive sorting
var a = ['B', 'C', 'a', 'd'];
a.sort(naturalCompare.caseInsensitive); // alias: a.sort(naturalCompare.i);
// Case-insensitive sorting
const chars = ['B', 'C', 'a', 'd'];
const naturalCompareCI = (a, b) => naturalCompare(a, b, {caseInsensitive: true});
chars.sort(naturalCompareCI);
// -> ['a', 'B', 'C', 'd']
// Note:
['a', 'A'].sort(naturalCompare.caseInsensitive); // -> ['a', 'A']
['A', 'a'].sort(naturalCompare.caseInsensitive); // -> ['A', 'a']
['a', 'A'].sort(naturalCompareCI); // -> ['a', 'A']
['A', 'a'].sort(naturalCompareCI); // -> ['A', 'a']

@@ -73,51 +75,37 @@

// -> 1
// Other inputs with the same ordering as this may yield a different number > 0
// (Other inputs with the same ordering as this example may yield a different number > 0)
// In most cases we want to sort an array of objects
var a = [
// Sorting an array of objects
const hotelRooms = [
{street: '350 5th Ave', room: 'A-1021'},
{street: '350 5th Ave', room: 'A-21046-b'}
];
// Sort by street (case-insensitive), then by room (case-sensitive)
a.sort(function(a, b) {
return (
naturalCompare.caseInsensitive(a.street, b.street) ||
naturalCompare(a.room, b.room)
);
});
hotelRooms.sort((a, b) => (
naturalCompare(a.street, b.street, {caseInsensitive: true}) ||
naturalCompare(a.room, b.room)
));
// When text transformation is needed or when doing a case-insensitive sort on a
// large array, it is best for performance to pre-compute the transformed text
// and store it in that object. This way, the text transformation will not be
// needed for every comparison when sorting.
var a = [
// large array of objects, it is best for performance to pre-compute the
// transformed text and store it on the object. This way, the text will not need
// to be transformed for every comparison while sorting.
const cars = [
{make: 'Audi', model: 'R8'},
{make: 'Porsche', model: '911 Turbo S'}
];
// Sort by make, then by model (both case-insensitive)
a.forEach(function(car) {
for (const car of cars) {
car.sortKey = (car.make + ' ' + car.model).toLowerCase();
});
a.sort(function(a, b) {
return naturalCompare(a.sortKey, b.sortKey);
});
```
}
cars.sort((a, b) => naturalCompare(a.sortKey, b.sortKey));
### Custom Alphabet
It is possible to configure a custom alphabet to achieve a desired character ordering.
```js
// Estonian alphabet
naturalCompare.alphabet = 'ABDEFGHIJKLMNOPRSŠZŽTUVÕÄÖÜXYabdefghijklmnoprsšzžtuvõäöüxy';
['t', 'z', 'x', 'õ'].sort(naturalCompare);
// -> ['z', 't', 'õ', 'x']
// Russian alphabet
naturalCompare.alphabet = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя';
['Ё', 'А', 'б', 'Б'].sort(naturalCompare);
// Using a custom alphabet (Russian alphabet)
const russianOpts = {
alphabet: 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя',
};
['Ё', 'А', 'б', 'Б'].sort((a, b) => naturalCompare(a, b, russianOpts));
// -> ['А', 'Б', 'Ё', 'б']

@@ -124,0 +112,0 @@ ```

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc