123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- 'use strict';
- const defaultAlphabetIndexMap = [];
- function isNumberCode(code) {
- return code >= 48/* '0' */ && code <= 57/* '9' */;
- }
- 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}'`);
- }
- const lengthA = a.length;
- const lengthB = b.length;
- let indexA = 0;
- let indexB = 0;
- let alphabetIndexMap = defaultAlphabetIndexMap;
- let firstDifferenceInLeadingZeros = 0;
- 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)) {
- if (!isNumberCode(charCodeB)) {
- return charCodeA - charCodeB;
- }
- let numStartA = indexA;
- let numStartB = indexB;
- while (charCodeA === 48/* '0' */ && ++numStartA < lengthA) {
- charCodeA = a.charCodeAt(numStartA);
- }
- while (charCodeB === 48/* '0' */ && ++numStartB < lengthB) {
- charCodeB = b.charCodeAt(numStartB);
- }
- if (numStartA !== numStartB && firstDifferenceInLeadingZeros === 0) {
- firstDifferenceInLeadingZeros = numStartA - numStartB;
- }
- let numEndA = numStartA;
- let numEndB = numStartB;
- while (numEndA < lengthA && isNumberCode(a.charCodeAt(numEndA))) {
- ++numEndA;
- }
- while (numEndB < lengthB && isNumberCode(b.charCodeAt(numEndB))) {
- ++numEndB;
- }
- let difference = numEndA - numStartA - numEndB + numStartB; // numA length - numB length
- if (difference !== 0) {
- return difference;
- }
- while (numStartA < numEndA) {
- difference = a.charCodeAt(numStartA++) - b.charCodeAt(numStartB++);
- if (difference !== 0) {
- return difference;
- }
- }
- indexA = numEndA;
- indexB = numEndB;
- continue;
- }
- if (charCodeA !== charCodeB) {
- if (
- charCodeA < alphabetIndexMap.length &&
- charCodeB < alphabetIndexMap.length &&
- alphabetIndexMap[charCodeA] !== -1 &&
- alphabetIndexMap[charCodeB] !== -1
- ) {
- return alphabetIndexMap[charCodeA] - alphabetIndexMap[charCodeB];
- }
- return charCodeA - charCodeB;
- }
- ++indexA;
- ++indexB;
- }
- if (indexA < lengthA) { // `b` is a substring of `a`
- return 1;
- }
- if (indexB < lengthB) { // `a` is a substring of `b`
- return -1;
- }
- return firstDifferenceInLeadingZeros;
- }
- const alphabetIndexMapCache = {};
- function buildAlphabetIndexMap(alphabet) {
- const existingMap = alphabetIndexMapCache[alphabet];
- if (existingMap !== undefined) {
- return existingMap;
- }
- const indexMap = [];
- const maxCharCode = alphabet.split('').reduce((maxCode, char) => {
- return Math.max(maxCode, char.charCodeAt(0));
- }, 0);
- for (let i = 0; i <= maxCharCode; i++) {
- indexMap.push(-1);
- }
- for (let i = 0; i < alphabet.length; i++) {
- indexMap[alphabet.charCodeAt(i)] = i;
- }
- alphabetIndexMapCache[alphabet] = indexMap;
- return indexMap;
- }
- module.exports = naturalCompare;
|