routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
type CompareFn = (valueA: unknown, valueB: unknown) => number;
|
||||
type OrderEnum = 'asc' | 'desc';
|
||||
type Order = OrderEnum | CompareFn;
|
||||
type CompareOptions = {
|
||||
order?: OrderEnum;
|
||||
locale?: Locale;
|
||||
} | OrderEnum | undefined;
|
||||
type Locale = string;
|
||||
type IdentifierFn<T> = (value: T) => unknown;
|
||||
type Identifier<T> = IdentifierFn<T> | keyof T | number;
|
||||
|
||||
/**
|
||||
* Creates a compare function that defines the natural sort order considering
|
||||
* the given `options` which may be passed to [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
|
||||
*/
|
||||
declare function compare(options?: CompareOptions): CompareFn;
|
||||
|
||||
/**
|
||||
* Creates an array of elements, natural sorted by specified identifiers and
|
||||
* the corresponding sort orders. This method implements a stable sort
|
||||
* algorithm, which means the original sort order of equal elements is
|
||||
* preserved.
|
||||
*/
|
||||
declare function orderBy<T>(collection: ReadonlyArray<T>, identifiers?: ReadonlyArray<Identifier<T>> | Identifier<T> | null, orders?: ReadonlyArray<Order> | Order | null, locale?: Locale): Array<T>;
|
||||
|
||||
export { type CompareFn, type CompareOptions, type Identifier, type Order, compare, orderBy };
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
/**
|
||||
* natural-orderby v5.0.0
|
||||
*
|
||||
* Copyright (c) Olaf Ennen
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
var compareNumbers = function compareNumbers(numberA, numberB) {
|
||||
if (numberA < numberB) {
|
||||
return -1;
|
||||
}
|
||||
if (numberA > numberB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareUnicode = function compareUnicode(stringA, stringB, locale) {
|
||||
var result = stringA.localeCompare(stringB, locale);
|
||||
return result ? result / Math.abs(result) : 0;
|
||||
};
|
||||
|
||||
var RE_NUMBERS = /(^0x[\da-fA-F]+$|^([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?(?!\.\d+)(?=\D|\s|$))|\d+)/g;
|
||||
var RE_LEADING_OR_TRAILING_WHITESPACES = /^\s+|\s+$/g; // trim pre-post whitespace
|
||||
var RE_WHITESPACES = /\s+/g; // normalize all whitespace to single ' ' character
|
||||
var RE_INT_OR_FLOAT = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/; // identify integers and floats
|
||||
var RE_DATE = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[/-]\d{1,4}[/-]\d{1,4}|^\w+, \w+ \d+, \d{4})/; // identify date strings
|
||||
var RE_LEADING_ZERO = /^0+[1-9]{1}[0-9]*$/;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
var RE_UNICODE_CHARACTERS = /[^\x00-\x80]/;
|
||||
|
||||
var stringCompare = function stringCompare(stringA, stringB) {
|
||||
if (stringA < stringB) {
|
||||
return -1;
|
||||
}
|
||||
if (stringA > stringB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareChunks = function compareChunks(chunksA, chunksB, locale) {
|
||||
var lengthA = chunksA.length;
|
||||
var lengthB = chunksB.length;
|
||||
var size = Math.min(lengthA, lengthB);
|
||||
for (var i = 0; i < size; i++) {
|
||||
var chunkA = chunksA[i];
|
||||
var chunkB = chunksB[i];
|
||||
if (chunkA.normalizedString !== chunkB.normalizedString) {
|
||||
if (chunkA.normalizedString === '' !== (chunkB.normalizedString === '')) {
|
||||
// empty strings have lowest value
|
||||
return chunkA.normalizedString === '' ? -1 : 1;
|
||||
}
|
||||
if (chunkA.parsedNumber !== undefined && chunkB.parsedNumber !== undefined) {
|
||||
// compare numbers
|
||||
var result = compareNumbers(chunkA.parsedNumber, chunkB.parsedNumber);
|
||||
if (result === 0) {
|
||||
// compare string value, if parsed numbers are equal
|
||||
// Example:
|
||||
// chunkA = { parsedNumber: 1, normalizedString: "001" }
|
||||
// chunkB = { parsedNumber: 1, normalizedString: "01" }
|
||||
// chunkA.parsedNumber === chunkB.parsedNumber
|
||||
// chunkA.normalizedString < chunkB.normalizedString
|
||||
return stringCompare(chunkA.normalizedString, chunkB.normalizedString);
|
||||
}
|
||||
return result;
|
||||
} else if (chunkA.parsedNumber !== undefined || chunkB.parsedNumber !== undefined) {
|
||||
// number < string
|
||||
return chunkA.parsedNumber !== undefined ? -1 : 1;
|
||||
} else if (RE_UNICODE_CHARACTERS.test(chunkA.normalizedString + chunkB.normalizedString)) {
|
||||
// use locale comparison only if one of the chunks contains unicode characters
|
||||
return compareUnicode(chunkA.normalizedString, chunkB.normalizedString, locale);
|
||||
} else {
|
||||
// use common string comparison for performance reason
|
||||
return stringCompare(chunkA.normalizedString, chunkB.normalizedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the chunks are equal so far, the one which has more chunks is greater than the other one
|
||||
if (lengthA > size || lengthB > size) {
|
||||
return lengthA <= size ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareOtherTypes = function compareOtherTypes(valueA, valueB) {
|
||||
if (!valueA.chunks ? valueB.chunks : !valueB.chunks) {
|
||||
return !valueA.chunks ? 1 : -1;
|
||||
}
|
||||
if (valueA.isNaN ? !valueB.isNaN : valueB.isNaN) {
|
||||
return valueA.isNaN ? -1 : 1;
|
||||
}
|
||||
if (valueA.isSymbol ? !valueB.isSymbol : valueB.isSymbol) {
|
||||
return valueA.isSymbol ? -1 : 1;
|
||||
}
|
||||
if (valueA.isObject ? !valueB.isObject : valueB.isObject) {
|
||||
return valueA.isObject ? -1 : 1;
|
||||
}
|
||||
if (valueA.isArray ? !valueB.isArray : valueB.isArray) {
|
||||
return valueA.isArray ? -1 : 1;
|
||||
}
|
||||
if (valueA.isFunction ? !valueB.isFunction : valueB.isFunction) {
|
||||
return valueA.isFunction ? -1 : 1;
|
||||
}
|
||||
if (valueA.isNull ? !valueB.isNull : valueB.isNull) {
|
||||
return valueA.isNull ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareValues = function compareValues(valueA, valueB, locale) {
|
||||
if (valueA.value === valueB.value) {
|
||||
return 0;
|
||||
}
|
||||
if (valueA.parsedNumber !== undefined && valueB.parsedNumber !== undefined) {
|
||||
return compareNumbers(valueA.parsedNumber, valueB.parsedNumber);
|
||||
}
|
||||
if (valueA.chunks && valueB.chunks) {
|
||||
return compareChunks(valueA.chunks, valueB.chunks, locale);
|
||||
}
|
||||
return compareOtherTypes(valueA, valueB);
|
||||
};
|
||||
|
||||
var normalizeAlphaChunk = function normalizeAlphaChunk(chunk) {
|
||||
return chunk.replace(RE_WHITESPACES, ' ').replace(RE_LEADING_OR_TRAILING_WHITESPACES, '');
|
||||
};
|
||||
|
||||
var parseNumber = function parseNumber(value) {
|
||||
if (value.length !== 0) {
|
||||
var parsedNumber = Number(value.replace(/_/g, ''));
|
||||
if (!Number.isNaN(parsedNumber)) {
|
||||
return parsedNumber;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
var normalizeNumericChunk = function normalizeNumericChunk(chunk, index, chunks) {
|
||||
if (RE_INT_OR_FLOAT.test(chunk)) {
|
||||
// don´t parse a number, if there´s a preceding decimal point
|
||||
// to keep significance
|
||||
// e.g. 1.0020, 1.020
|
||||
if (!RE_LEADING_ZERO.test(chunk) || index === 0 || chunks[index - 1] !== '.') {
|
||||
return parseNumber(chunk) || 0;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
var createChunkMap = function createChunkMap(chunk, index, chunks) {
|
||||
return {
|
||||
parsedNumber: normalizeNumericChunk(chunk, index, chunks),
|
||||
normalizedString: normalizeAlphaChunk(chunk)
|
||||
};
|
||||
};
|
||||
|
||||
var createChunks = function createChunks(value) {
|
||||
return value.replace(RE_NUMBERS, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
|
||||
};
|
||||
|
||||
var createChunkMaps = function createChunkMaps(value) {
|
||||
var chunksMaps = createChunks(value).map(createChunkMap);
|
||||
return chunksMaps;
|
||||
};
|
||||
|
||||
var isFunction = function isFunction(value) {
|
||||
return typeof value === 'function';
|
||||
};
|
||||
|
||||
var isNaN = function isNaN(value) {
|
||||
return Number.isNaN(value) || value instanceof Number && Number.isNaN(value.valueOf());
|
||||
};
|
||||
|
||||
var isNull = function isNull(value) {
|
||||
return value === null;
|
||||
};
|
||||
|
||||
var isObject = function isObject(value) {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Number) && !(value instanceof String) && !(value instanceof Boolean) && !(value instanceof Date);
|
||||
};
|
||||
|
||||
var isSymbol = function isSymbol(value) {
|
||||
return typeof value === 'symbol';
|
||||
};
|
||||
|
||||
var isUndefined = function isUndefined(value) {
|
||||
return value === undefined;
|
||||
};
|
||||
|
||||
var parseDate = function parseDate(value) {
|
||||
try {
|
||||
var parsedDate = Date.parse(value);
|
||||
if (!Number.isNaN(parsedDate)) {
|
||||
if (RE_DATE.test(value)) {
|
||||
return parsedDate;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
} catch (_unused) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
var numberify = function numberify(value) {
|
||||
var parsedNumber = parseNumber(value);
|
||||
if (parsedNumber !== undefined) {
|
||||
return parsedNumber;
|
||||
}
|
||||
return parseDate(value);
|
||||
};
|
||||
|
||||
var stringify = function stringify(value) {
|
||||
if (typeof value === 'boolean' || value instanceof Boolean) {
|
||||
return Number(value).toString();
|
||||
}
|
||||
if (typeof value === 'number' || value instanceof Number) {
|
||||
return value.toString();
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return value.getTime().toString();
|
||||
}
|
||||
if (typeof value === 'string' || value instanceof String) {
|
||||
return value.toLowerCase().replace(RE_LEADING_OR_TRAILING_WHITESPACES, '');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
var getMappedValueRecord = function getMappedValueRecord(value) {
|
||||
if (typeof value === 'string' || value instanceof String || (typeof value === 'number' || value instanceof Number) && !isNaN(value) || typeof value === 'boolean' || value instanceof Boolean || value instanceof Date) {
|
||||
var stringValue = stringify(value);
|
||||
var parsedNumber = numberify(stringValue);
|
||||
var chunks = createChunkMaps(parsedNumber ? "" + parsedNumber : stringValue);
|
||||
return {
|
||||
parsedNumber: parsedNumber,
|
||||
chunks: chunks,
|
||||
value: value
|
||||
};
|
||||
}
|
||||
return {
|
||||
isArray: Array.isArray(value),
|
||||
isFunction: isFunction(value),
|
||||
isNaN: isNaN(value),
|
||||
isNull: isNull(value),
|
||||
isObject: isObject(value),
|
||||
isSymbol: isSymbol(value),
|
||||
isUndefined: isUndefined(value),
|
||||
value: value
|
||||
};
|
||||
};
|
||||
|
||||
var baseCompare = function baseCompare(options) {
|
||||
return function (valueA, valueB) {
|
||||
var a = getMappedValueRecord(valueA);
|
||||
var b = getMappedValueRecord(valueB);
|
||||
var result = compareValues(a, b, options.locale);
|
||||
return result * (options.order === 'desc' ? -1 : 1);
|
||||
};
|
||||
};
|
||||
|
||||
var isValidOrder = function isValidOrder(value) {
|
||||
return typeof value === 'string' && (value === 'asc' || value === 'desc');
|
||||
};
|
||||
var getOptions = function getOptions(customOptions) {
|
||||
var order = 'asc';
|
||||
var locale; // = 'en';
|
||||
if (typeof customOptions === 'string' && isValidOrder(customOptions)) {
|
||||
order = customOptions;
|
||||
} else if (customOptions && typeof customOptions === 'object') {
|
||||
if (customOptions.order && isValidOrder(customOptions.order)) {
|
||||
order = customOptions.order;
|
||||
}
|
||||
if (customOptions.locale && customOptions.locale.length > 0) {
|
||||
locale = customOptions.locale;
|
||||
}
|
||||
}
|
||||
return {
|
||||
order: order,
|
||||
locale: locale
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a compare function that defines the natural sort order considering
|
||||
* the given `options` which may be passed to [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
|
||||
*/
|
||||
function compare(options) {
|
||||
var validatedOptions = getOptions(options);
|
||||
return baseCompare(validatedOptions);
|
||||
}
|
||||
|
||||
var compareMultiple = function compareMultiple(recordA, recordB, orders, locale) {
|
||||
var indexA = recordA.index,
|
||||
valuesA = recordA.values;
|
||||
var indexB = recordB.index,
|
||||
valuesB = recordB.values;
|
||||
var length = valuesA.length;
|
||||
var ordersLength = orders.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
var order = i < ordersLength ? orders[i] : null;
|
||||
if (order && typeof order === 'function') {
|
||||
var result = order(valuesA[i].value, valuesB[i].value);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
var _result = compareValues(valuesA[i], valuesB[i], locale);
|
||||
if (_result) {
|
||||
return _result * (order === 'desc' ? -1 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return indexA - indexB;
|
||||
};
|
||||
|
||||
var createIdentifierFn = function createIdentifierFn(identifier) {
|
||||
if (typeof identifier === 'function') {
|
||||
// identifier is already a lookup function
|
||||
return identifier;
|
||||
}
|
||||
return function (value) {
|
||||
if (Array.isArray(value)) {
|
||||
var index = Number(identifier);
|
||||
if (Number.isInteger(index)) {
|
||||
return value[index];
|
||||
}
|
||||
} else if (value && typeof value === 'object') {
|
||||
var result = Object.getOwnPropertyDescriptor(value, identifier);
|
||||
return result == null ? void 0 : result.value;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
var getElementByIndex = function getElementByIndex(collection, index) {
|
||||
return collection[index];
|
||||
};
|
||||
|
||||
var getValueByIdentifier = function getValueByIdentifier(value, getValue) {
|
||||
return getValue(value);
|
||||
};
|
||||
|
||||
var baseOrderBy = function baseOrderBy(collection, identifiers, orders, locale) {
|
||||
var identifierFns = identifiers.length ? identifiers.map(createIdentifierFn) : [function (value) {
|
||||
return value;
|
||||
}];
|
||||
|
||||
// temporary array holds elements with position and sort-values
|
||||
var mappedCollection = collection.map(function (element, index) {
|
||||
var values = identifierFns.map(function (identifier) {
|
||||
return getValueByIdentifier(element, identifier);
|
||||
}).map(getMappedValueRecord);
|
||||
return {
|
||||
index: index,
|
||||
values: values
|
||||
};
|
||||
});
|
||||
|
||||
// iterate over values and compare values until a != b or last value reached
|
||||
mappedCollection.sort(function (recordA, recordB) {
|
||||
return compareMultiple(recordA, recordB, orders, locale);
|
||||
});
|
||||
return mappedCollection.map(function (element) {
|
||||
return getElementByIndex(collection, element.index);
|
||||
});
|
||||
};
|
||||
|
||||
var getIdentifiers = function getIdentifiers(identifiers) {
|
||||
if (!identifiers) {
|
||||
return [];
|
||||
}
|
||||
var identifierList = !Array.isArray(identifiers) ? [identifiers] : [].concat(identifiers);
|
||||
if (identifierList.some(function (identifier) {
|
||||
return typeof identifier !== 'string' && typeof identifier !== 'number' && typeof identifier !== 'function';
|
||||
})) {
|
||||
return [];
|
||||
}
|
||||
return identifierList;
|
||||
};
|
||||
|
||||
var getOrders = function getOrders(orders) {
|
||||
if (!orders) {
|
||||
return [];
|
||||
}
|
||||
var orderList = !Array.isArray(orders) ? [orders] : [].concat(orders);
|
||||
if (orderList.some(function (order) {
|
||||
return order !== 'asc' && order !== 'desc' && typeof order !== 'function';
|
||||
})) {
|
||||
return [];
|
||||
}
|
||||
return orderList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an array of elements, natural sorted by specified identifiers and
|
||||
* the corresponding sort orders. This method implements a stable sort
|
||||
* algorithm, which means the original sort order of equal elements is
|
||||
* preserved.
|
||||
*/
|
||||
function orderBy(collection, identifiers, orders, locale) {
|
||||
if (!collection || !Array.isArray(collection)) {
|
||||
return [];
|
||||
}
|
||||
var validatedIdentifiers = getIdentifiers(identifiers);
|
||||
var validatedOrders = getOrders(orders);
|
||||
return baseOrderBy(collection, validatedIdentifiers, validatedOrders, locale);
|
||||
}
|
||||
|
||||
export { compare, orderBy };
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* natural-orderby v5.0.0
|
||||
*
|
||||
* Copyright (c) Olaf Ennen
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable n/no-missing-require */
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./umd/natural-orderby.production.min.js');
|
||||
} else {
|
||||
module.exports = require('./umd/natural-orderby.development.js');
|
||||
}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
/**
|
||||
* natural-orderby v5.0.0
|
||||
*
|
||||
* Copyright (c) Olaf Ennen
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
const compareNumbers = (numberA, numberB) => {
|
||||
if (numberA < numberB) {
|
||||
return -1;
|
||||
}
|
||||
if (numberA > numberB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const compareUnicode = (stringA, stringB, locale) => {
|
||||
const result = stringA.localeCompare(stringB, locale);
|
||||
return result ? result / Math.abs(result) : 0;
|
||||
};
|
||||
|
||||
const RE_NUMBERS = /(^0x[\da-fA-F]+$|^([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?(?!\.\d+)(?=\D|\s|$))|\d+)/g;
|
||||
const RE_LEADING_OR_TRAILING_WHITESPACES = /^\s+|\s+$/g; // trim pre-post whitespace
|
||||
const RE_WHITESPACES = /\s+/g; // normalize all whitespace to single ' ' character
|
||||
const RE_INT_OR_FLOAT = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/; // identify integers and floats
|
||||
const RE_DATE = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[/-]\d{1,4}[/-]\d{1,4}|^\w+, \w+ \d+, \d{4})/; // identify date strings
|
||||
const RE_LEADING_ZERO = /^0+[1-9]{1}[0-9]*$/;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const RE_UNICODE_CHARACTERS = /[^\x00-\x80]/;
|
||||
|
||||
const stringCompare = (stringA, stringB) => {
|
||||
if (stringA < stringB) {
|
||||
return -1;
|
||||
}
|
||||
if (stringA > stringB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const compareChunks = (chunksA, chunksB, locale) => {
|
||||
const lengthA = chunksA.length;
|
||||
const lengthB = chunksB.length;
|
||||
const size = Math.min(lengthA, lengthB);
|
||||
for (let i = 0; i < size; i++) {
|
||||
const chunkA = chunksA[i];
|
||||
const chunkB = chunksB[i];
|
||||
if (chunkA.normalizedString !== chunkB.normalizedString) {
|
||||
if (chunkA.normalizedString === '' !== (chunkB.normalizedString === '')) {
|
||||
// empty strings have lowest value
|
||||
return chunkA.normalizedString === '' ? -1 : 1;
|
||||
}
|
||||
if (chunkA.parsedNumber !== undefined && chunkB.parsedNumber !== undefined) {
|
||||
// compare numbers
|
||||
const result = compareNumbers(chunkA.parsedNumber, chunkB.parsedNumber);
|
||||
if (result === 0) {
|
||||
// compare string value, if parsed numbers are equal
|
||||
// Example:
|
||||
// chunkA = { parsedNumber: 1, normalizedString: "001" }
|
||||
// chunkB = { parsedNumber: 1, normalizedString: "01" }
|
||||
// chunkA.parsedNumber === chunkB.parsedNumber
|
||||
// chunkA.normalizedString < chunkB.normalizedString
|
||||
return stringCompare(chunkA.normalizedString, chunkB.normalizedString);
|
||||
}
|
||||
return result;
|
||||
} else if (chunkA.parsedNumber !== undefined || chunkB.parsedNumber !== undefined) {
|
||||
// number < string
|
||||
return chunkA.parsedNumber !== undefined ? -1 : 1;
|
||||
} else if (RE_UNICODE_CHARACTERS.test(chunkA.normalizedString + chunkB.normalizedString)) {
|
||||
// use locale comparison only if one of the chunks contains unicode characters
|
||||
return compareUnicode(chunkA.normalizedString, chunkB.normalizedString, locale);
|
||||
} else {
|
||||
// use common string comparison for performance reason
|
||||
return stringCompare(chunkA.normalizedString, chunkB.normalizedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the chunks are equal so far, the one which has more chunks is greater than the other one
|
||||
if (lengthA > size || lengthB > size) {
|
||||
return lengthA <= size ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const compareOtherTypes = (valueA, valueB) => {
|
||||
if (!valueA.chunks ? valueB.chunks : !valueB.chunks) {
|
||||
return !valueA.chunks ? 1 : -1;
|
||||
}
|
||||
if (valueA.isNaN ? !valueB.isNaN : valueB.isNaN) {
|
||||
return valueA.isNaN ? -1 : 1;
|
||||
}
|
||||
if (valueA.isSymbol ? !valueB.isSymbol : valueB.isSymbol) {
|
||||
return valueA.isSymbol ? -1 : 1;
|
||||
}
|
||||
if (valueA.isObject ? !valueB.isObject : valueB.isObject) {
|
||||
return valueA.isObject ? -1 : 1;
|
||||
}
|
||||
if (valueA.isArray ? !valueB.isArray : valueB.isArray) {
|
||||
return valueA.isArray ? -1 : 1;
|
||||
}
|
||||
if (valueA.isFunction ? !valueB.isFunction : valueB.isFunction) {
|
||||
return valueA.isFunction ? -1 : 1;
|
||||
}
|
||||
if (valueA.isNull ? !valueB.isNull : valueB.isNull) {
|
||||
return valueA.isNull ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const compareValues = (valueA, valueB, locale) => {
|
||||
if (valueA.value === valueB.value) {
|
||||
return 0;
|
||||
}
|
||||
if (valueA.parsedNumber !== undefined && valueB.parsedNumber !== undefined) {
|
||||
return compareNumbers(valueA.parsedNumber, valueB.parsedNumber);
|
||||
}
|
||||
if (valueA.chunks && valueB.chunks) {
|
||||
return compareChunks(valueA.chunks, valueB.chunks, locale);
|
||||
}
|
||||
return compareOtherTypes(valueA, valueB);
|
||||
};
|
||||
|
||||
const normalizeAlphaChunk = chunk => {
|
||||
return chunk.replace(RE_WHITESPACES, ' ').replace(RE_LEADING_OR_TRAILING_WHITESPACES, '');
|
||||
};
|
||||
|
||||
const parseNumber = value => {
|
||||
if (value.length !== 0) {
|
||||
const parsedNumber = Number(value.replace(/_/g, ''));
|
||||
if (!Number.isNaN(parsedNumber)) {
|
||||
return parsedNumber;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const normalizeNumericChunk = (chunk, index, chunks) => {
|
||||
if (RE_INT_OR_FLOAT.test(chunk)) {
|
||||
// don´t parse a number, if there´s a preceding decimal point
|
||||
// to keep significance
|
||||
// e.g. 1.0020, 1.020
|
||||
if (!RE_LEADING_ZERO.test(chunk) || index === 0 || chunks[index - 1] !== '.') {
|
||||
return parseNumber(chunk) || 0;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const createChunkMap = (chunk, index, chunks) => ({
|
||||
parsedNumber: normalizeNumericChunk(chunk, index, chunks),
|
||||
normalizedString: normalizeAlphaChunk(chunk)
|
||||
});
|
||||
|
||||
const createChunks = value => value.replace(RE_NUMBERS, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
|
||||
|
||||
const createChunkMaps = value => {
|
||||
const chunksMaps = createChunks(value).map(createChunkMap);
|
||||
return chunksMaps;
|
||||
};
|
||||
|
||||
const isFunction = value => typeof value === 'function';
|
||||
|
||||
const isNaN = value => Number.isNaN(value) || value instanceof Number && Number.isNaN(value.valueOf());
|
||||
|
||||
const isNull = value => value === null;
|
||||
|
||||
const isObject = value => value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Number) && !(value instanceof String) && !(value instanceof Boolean) && !(value instanceof Date);
|
||||
|
||||
const isSymbol = value => typeof value === 'symbol';
|
||||
|
||||
const isUndefined = value => value === undefined;
|
||||
|
||||
const parseDate = value => {
|
||||
try {
|
||||
const parsedDate = Date.parse(value);
|
||||
if (!Number.isNaN(parsedDate)) {
|
||||
if (RE_DATE.test(value)) {
|
||||
return parsedDate;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const numberify = value => {
|
||||
const parsedNumber = parseNumber(value);
|
||||
if (parsedNumber !== undefined) {
|
||||
return parsedNumber;
|
||||
}
|
||||
return parseDate(value);
|
||||
};
|
||||
|
||||
const stringify = value => {
|
||||
if (typeof value === 'boolean' || value instanceof Boolean) {
|
||||
return Number(value).toString();
|
||||
}
|
||||
if (typeof value === 'number' || value instanceof Number) {
|
||||
return value.toString();
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return value.getTime().toString();
|
||||
}
|
||||
if (typeof value === 'string' || value instanceof String) {
|
||||
return value.toLowerCase().replace(RE_LEADING_OR_TRAILING_WHITESPACES, '');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const getMappedValueRecord = value => {
|
||||
if (typeof value === 'string' || value instanceof String || (typeof value === 'number' || value instanceof Number) && !isNaN(value) || typeof value === 'boolean' || value instanceof Boolean || value instanceof Date) {
|
||||
const stringValue = stringify(value);
|
||||
const parsedNumber = numberify(stringValue);
|
||||
const chunks = createChunkMaps(parsedNumber ? `${parsedNumber}` : stringValue);
|
||||
return {
|
||||
parsedNumber,
|
||||
chunks,
|
||||
value
|
||||
};
|
||||
}
|
||||
return {
|
||||
isArray: Array.isArray(value),
|
||||
isFunction: isFunction(value),
|
||||
isNaN: isNaN(value),
|
||||
isNull: isNull(value),
|
||||
isObject: isObject(value),
|
||||
isSymbol: isSymbol(value),
|
||||
isUndefined: isUndefined(value),
|
||||
value
|
||||
};
|
||||
};
|
||||
|
||||
const baseCompare = options => (valueA, valueB) => {
|
||||
const a = getMappedValueRecord(valueA);
|
||||
const b = getMappedValueRecord(valueB);
|
||||
const result = compareValues(a, b, options.locale);
|
||||
return result * (options.order === 'desc' ? -1 : 1);
|
||||
};
|
||||
|
||||
const isValidOrder = value => typeof value === 'string' && (value === 'asc' || value === 'desc');
|
||||
const getOptions = customOptions => {
|
||||
let order = 'asc';
|
||||
let locale; // = 'en';
|
||||
if (typeof customOptions === 'string' && isValidOrder(customOptions)) {
|
||||
order = customOptions;
|
||||
} else if (customOptions && typeof customOptions === 'object') {
|
||||
if (customOptions.order && isValidOrder(customOptions.order)) {
|
||||
order = customOptions.order;
|
||||
}
|
||||
if (customOptions.locale && customOptions.locale.length > 0) {
|
||||
locale = customOptions.locale;
|
||||
}
|
||||
}
|
||||
return {
|
||||
order,
|
||||
locale
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a compare function that defines the natural sort order considering
|
||||
* the given `options` which may be passed to [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
|
||||
*/
|
||||
function compare(options) {
|
||||
const validatedOptions = getOptions(options);
|
||||
return baseCompare(validatedOptions);
|
||||
}
|
||||
|
||||
const compareMultiple = (recordA, recordB, orders, locale) => {
|
||||
const {
|
||||
index: indexA,
|
||||
values: valuesA
|
||||
} = recordA;
|
||||
const {
|
||||
index: indexB,
|
||||
values: valuesB
|
||||
} = recordB;
|
||||
const {
|
||||
length
|
||||
} = valuesA;
|
||||
const ordersLength = orders.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
const order = i < ordersLength ? orders[i] : null;
|
||||
if (order && typeof order === 'function') {
|
||||
const result = order(valuesA[i].value, valuesB[i].value);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
const result = compareValues(valuesA[i], valuesB[i], locale);
|
||||
if (result) {
|
||||
return result * (order === 'desc' ? -1 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return indexA - indexB;
|
||||
};
|
||||
|
||||
const createIdentifierFn = identifier => {
|
||||
if (typeof identifier === 'function') {
|
||||
// identifier is already a lookup function
|
||||
return identifier;
|
||||
}
|
||||
return value => {
|
||||
if (Array.isArray(value)) {
|
||||
const index = Number(identifier);
|
||||
if (Number.isInteger(index)) {
|
||||
return value[index];
|
||||
}
|
||||
} else if (value && typeof value === 'object') {
|
||||
const result = Object.getOwnPropertyDescriptor(value, identifier);
|
||||
return result?.value;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
const getElementByIndex = (collection, index) => collection[index];
|
||||
|
||||
const getValueByIdentifier = (value, getValue) => getValue(value);
|
||||
|
||||
const baseOrderBy = (collection, identifiers, orders, locale) => {
|
||||
const identifierFns = identifiers.length ? identifiers.map(createIdentifierFn) : [value => value];
|
||||
|
||||
// temporary array holds elements with position and sort-values
|
||||
const mappedCollection = collection.map((element, index) => {
|
||||
const values = identifierFns.map(identifier => getValueByIdentifier(element, identifier)).map(getMappedValueRecord);
|
||||
return {
|
||||
index,
|
||||
values
|
||||
};
|
||||
});
|
||||
|
||||
// iterate over values and compare values until a != b or last value reached
|
||||
mappedCollection.sort((recordA, recordB) => compareMultiple(recordA, recordB, orders, locale));
|
||||
return mappedCollection.map(element => getElementByIndex(collection, element.index));
|
||||
};
|
||||
|
||||
const getIdentifiers = identifiers => {
|
||||
if (!identifiers) {
|
||||
return [];
|
||||
}
|
||||
const identifierList = !Array.isArray(identifiers) ? [identifiers] : [...identifiers];
|
||||
if (identifierList.some(identifier => typeof identifier !== 'string' && typeof identifier !== 'number' && typeof identifier !== 'function')) {
|
||||
return [];
|
||||
}
|
||||
return identifierList;
|
||||
};
|
||||
|
||||
const getOrders = orders => {
|
||||
if (!orders) {
|
||||
return [];
|
||||
}
|
||||
const orderList = !Array.isArray(orders) ? [orders] : [...orders];
|
||||
if (orderList.some(order => order !== 'asc' && order !== 'desc' && typeof order !== 'function')) {
|
||||
return [];
|
||||
}
|
||||
return orderList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an array of elements, natural sorted by specified identifiers and
|
||||
* the corresponding sort orders. This method implements a stable sort
|
||||
* algorithm, which means the original sort order of equal elements is
|
||||
* preserved.
|
||||
*/
|
||||
function orderBy(collection, identifiers, orders, locale) {
|
||||
if (!collection || !Array.isArray(collection)) {
|
||||
return [];
|
||||
}
|
||||
const validatedIdentifiers = getIdentifiers(identifiers);
|
||||
const validatedOrders = getOrders(orders);
|
||||
return baseOrderBy(collection, validatedIdentifiers, validatedOrders, locale);
|
||||
}
|
||||
|
||||
export { compare, orderBy };
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* natural-orderby v5.0.0
|
||||
*
|
||||
* Copyright (c) Olaf Ennen
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
const e=(e,r)=>e<r?-1:e>r?1:0,r=(e,r,n)=>{const t=e.localeCompare(r,n);return t?t/Math.abs(t):0},n=/(^0x[\da-fA-F]+$|^([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?(?!\.\d+)(?=\D|\s|$))|\d+)/g,t=/^\s+|\s+$/g,i=/\s+/g,o=/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/,s=/(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[/-]\d{1,4}[/-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,a=/^0+[1-9]{1}[0-9]*$/,u=/[^\x00-\x80]/,c=(e,r)=>e<r?-1:e>r?1:0,l=(n,t,i)=>n.value===t.value?0:void 0!==n.parsedNumber&&void 0!==t.parsedNumber?e(n.parsedNumber,t.parsedNumber):n.chunks&&t.chunks?((n,t,i)=>{const o=n.length,s=t.length,a=Math.min(o,s);for(let l=0;l<a;l++){const o=n[l],s=t[l];if(o.normalizedString!==s.normalizedString){if(""===o.normalizedString!=(""===s.normalizedString))return""===o.normalizedString?-1:1;if(void 0!==o.parsedNumber&&void 0!==s.parsedNumber){const r=e(o.parsedNumber,s.parsedNumber);return 0===r?c(o.normalizedString,s.normalizedString):r}return void 0!==o.parsedNumber||void 0!==s.parsedNumber?void 0!==o.parsedNumber?-1:1:u.test(o.normalizedString+s.normalizedString)?r(o.normalizedString,s.normalizedString,i):c(o.normalizedString,s.normalizedString)}}return o>a||s>a?o<=a?-1:1:0})(n.chunks,t.chunks,i):((e,r)=>(e.chunks?!r.chunks:r.chunks)?e.chunks?-1:1:(e.isNaN?!r.isNaN:r.isNaN)?e.isNaN?-1:1:(e.isSymbol?!r.isSymbol:r.isSymbol)?e.isSymbol?-1:1:(e.isObject?!r.isObject:r.isObject)?e.isObject?-1:1:(e.isArray?!r.isArray:r.isArray)?e.isArray?-1:1:(e.isFunction?!r.isFunction:r.isFunction)?e.isFunction?-1:1:(e.isNull?!r.isNull:r.isNull)?e.isNull?-1:1:0)(n,t),d=e=>e.replace(i," ").replace(t,""),f=e=>{if(0!==e.length){const r=Number(e.replace(/_/g,""));if(!Number.isNaN(r))return r}},m=(e,r,n)=>{if(o.test(e)&&(!a.test(e)||0===r||"."!==n[r-1]))return f(e)||0},p=(e,r,n)=>({parsedNumber:m(e,r,n),normalizedString:d(e)}),N=e=>{const r=(e=>e.replace(n,"\0$1\0").replace(/\0$/,"").replace(/^\0/,"").split("\0"))(e).map(p);return r},b=e=>"function"==typeof e,y=e=>Number.isNaN(e)||e instanceof Number&&Number.isNaN(e.valueOf()),g=e=>null===e,S=e=>!(null===e||"object"!=typeof e||Array.isArray(e)||e instanceof Number||e instanceof String||e instanceof Boolean||e instanceof Date),v=e=>"symbol"==typeof e,h=e=>void 0===e,A=e=>{const r=f(e);return void 0!==r?r:(e=>{try{const r=Date.parse(e);return!Number.isNaN(r)&&s.test(e)?r:void 0}catch{return}})(e)},z=e=>{if("string"==typeof e||e instanceof String||("number"==typeof e||e instanceof Number)&&!y(e)||"boolean"==typeof e||e instanceof Boolean||e instanceof Date){const r=(e=>"boolean"==typeof e||e instanceof Boolean?Number(e).toString():"number"==typeof e||e instanceof Number?e.toString():e instanceof Date?e.getTime().toString():"string"==typeof e||e instanceof String?e.toLowerCase().replace(t,""):"")(e),n=A(r);return{parsedNumber:n,chunks:N(n?`${n}`:r),value:e}}return{isArray:Array.isArray(e),isFunction:b(e),isNaN:y(e),isNull:g(e),isObject:S(e),isSymbol:v(e),isUndefined:h(e),value:e}},j=e=>"string"==typeof e&&("asc"===e||"desc"===e);function k(e){return(e=>(r,n)=>{const t=z(r),i=z(n);return l(t,i,e.locale)*("desc"===e.order?-1:1)})((e=>{let r,n="asc";return"string"==typeof e&&j(e)?n=e:e&&"object"==typeof e&&(e.order&&j(e.order)&&(n=e.order),e.locale&&e.locale.length>0&&(r=e.locale)),{order:n,locale:r}})(e))}const w=e=>"function"==typeof e?e:r=>{if(Array.isArray(r)){const n=Number(e);if(Number.isInteger(n))return r[n]}else if(r&&"object"==typeof r){const n=Object.getOwnPropertyDescriptor(r,e);return n?.value}return r},x=(e,r,n,t)=>{const i=r.length?r.map(w):[e=>e],o=e.map(((e,r)=>({index:r,values:i.map((r=>r(e))).map(z)})));return o.sort(((e,r)=>((e,r,n,t)=>{const{index:i,values:o}=e,{index:s,values:a}=r,{length:u}=o,c=n.length;for(let d=0;d<u;d++){const e=d<c?n[d]:null;if(e&&"function"==typeof e){const r=e(o[d].value,a[d].value);if(r)return r}else{const r=l(o[d],a[d],t);if(r)return r*("desc"===e?-1:1)}}return i-s})(e,r,n,t))),o.map((r=>((e,r)=>e[r])(e,r.index)))};function O(e,r,n,t){if(!e||!Array.isArray(e))return[];const i=(e=>{if(!e)return[];const r=Array.isArray(e)?[...e]:[e];return r.some((e=>"string"!=typeof e&&"number"!=typeof e&&"function"!=typeof e))?[]:r})(r),o=(e=>{if(!e)return[];const r=Array.isArray(e)?[...e]:[e];return r.some((e=>"asc"!==e&&"desc"!==e&&"function"!=typeof e))?[]:r})(n);return x(e,i,o,t)}export{k as compare,O as orderBy};
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
/**
|
||||
* natural-orderby v5.0.0
|
||||
*
|
||||
* Copyright (c) Olaf Ennen
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.naturalOrderBy = {}));
|
||||
})(this, (function (exports) { 'use strict';
|
||||
|
||||
var compareNumbers = function compareNumbers(numberA, numberB) {
|
||||
if (numberA < numberB) {
|
||||
return -1;
|
||||
}
|
||||
if (numberA > numberB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareUnicode = function compareUnicode(stringA, stringB, locale) {
|
||||
var result = stringA.localeCompare(stringB, locale);
|
||||
return result ? result / Math.abs(result) : 0;
|
||||
};
|
||||
|
||||
var RE_NUMBERS = /(^0x[\da-fA-F]+$|^([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?(?!\.\d+)(?=\D|\s|$))|\d+)/g;
|
||||
var RE_LEADING_OR_TRAILING_WHITESPACES = /^\s+|\s+$/g; // trim pre-post whitespace
|
||||
var RE_WHITESPACES = /\s+/g; // normalize all whitespace to single ' ' character
|
||||
var RE_INT_OR_FLOAT = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/; // identify integers and floats
|
||||
var RE_DATE = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[/-]\d{1,4}[/-]\d{1,4}|^\w+, \w+ \d+, \d{4})/; // identify date strings
|
||||
var RE_LEADING_ZERO = /^0+[1-9]{1}[0-9]*$/;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
var RE_UNICODE_CHARACTERS = /[^\x00-\x80]/;
|
||||
|
||||
var stringCompare = function stringCompare(stringA, stringB) {
|
||||
if (stringA < stringB) {
|
||||
return -1;
|
||||
}
|
||||
if (stringA > stringB) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareChunks = function compareChunks(chunksA, chunksB, locale) {
|
||||
var lengthA = chunksA.length;
|
||||
var lengthB = chunksB.length;
|
||||
var size = Math.min(lengthA, lengthB);
|
||||
for (var i = 0; i < size; i++) {
|
||||
var chunkA = chunksA[i];
|
||||
var chunkB = chunksB[i];
|
||||
if (chunkA.normalizedString !== chunkB.normalizedString) {
|
||||
if (chunkA.normalizedString === '' !== (chunkB.normalizedString === '')) {
|
||||
// empty strings have lowest value
|
||||
return chunkA.normalizedString === '' ? -1 : 1;
|
||||
}
|
||||
if (chunkA.parsedNumber !== undefined && chunkB.parsedNumber !== undefined) {
|
||||
// compare numbers
|
||||
var result = compareNumbers(chunkA.parsedNumber, chunkB.parsedNumber);
|
||||
if (result === 0) {
|
||||
// compare string value, if parsed numbers are equal
|
||||
// Example:
|
||||
// chunkA = { parsedNumber: 1, normalizedString: "001" }
|
||||
// chunkB = { parsedNumber: 1, normalizedString: "01" }
|
||||
// chunkA.parsedNumber === chunkB.parsedNumber
|
||||
// chunkA.normalizedString < chunkB.normalizedString
|
||||
return stringCompare(chunkA.normalizedString, chunkB.normalizedString);
|
||||
}
|
||||
return result;
|
||||
} else if (chunkA.parsedNumber !== undefined || chunkB.parsedNumber !== undefined) {
|
||||
// number < string
|
||||
return chunkA.parsedNumber !== undefined ? -1 : 1;
|
||||
} else if (RE_UNICODE_CHARACTERS.test(chunkA.normalizedString + chunkB.normalizedString)) {
|
||||
// use locale comparison only if one of the chunks contains unicode characters
|
||||
return compareUnicode(chunkA.normalizedString, chunkB.normalizedString, locale);
|
||||
} else {
|
||||
// use common string comparison for performance reason
|
||||
return stringCompare(chunkA.normalizedString, chunkB.normalizedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the chunks are equal so far, the one which has more chunks is greater than the other one
|
||||
if (lengthA > size || lengthB > size) {
|
||||
return lengthA <= size ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareOtherTypes = function compareOtherTypes(valueA, valueB) {
|
||||
if (!valueA.chunks ? valueB.chunks : !valueB.chunks) {
|
||||
return !valueA.chunks ? 1 : -1;
|
||||
}
|
||||
if (valueA.isNaN ? !valueB.isNaN : valueB.isNaN) {
|
||||
return valueA.isNaN ? -1 : 1;
|
||||
}
|
||||
if (valueA.isSymbol ? !valueB.isSymbol : valueB.isSymbol) {
|
||||
return valueA.isSymbol ? -1 : 1;
|
||||
}
|
||||
if (valueA.isObject ? !valueB.isObject : valueB.isObject) {
|
||||
return valueA.isObject ? -1 : 1;
|
||||
}
|
||||
if (valueA.isArray ? !valueB.isArray : valueB.isArray) {
|
||||
return valueA.isArray ? -1 : 1;
|
||||
}
|
||||
if (valueA.isFunction ? !valueB.isFunction : valueB.isFunction) {
|
||||
return valueA.isFunction ? -1 : 1;
|
||||
}
|
||||
if (valueA.isNull ? !valueB.isNull : valueB.isNull) {
|
||||
return valueA.isNull ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var compareValues = function compareValues(valueA, valueB, locale) {
|
||||
if (valueA.value === valueB.value) {
|
||||
return 0;
|
||||
}
|
||||
if (valueA.parsedNumber !== undefined && valueB.parsedNumber !== undefined) {
|
||||
return compareNumbers(valueA.parsedNumber, valueB.parsedNumber);
|
||||
}
|
||||
if (valueA.chunks && valueB.chunks) {
|
||||
return compareChunks(valueA.chunks, valueB.chunks, locale);
|
||||
}
|
||||
return compareOtherTypes(valueA, valueB);
|
||||
};
|
||||
|
||||
var normalizeAlphaChunk = function normalizeAlphaChunk(chunk) {
|
||||
return chunk.replace(RE_WHITESPACES, ' ').replace(RE_LEADING_OR_TRAILING_WHITESPACES, '');
|
||||
};
|
||||
|
||||
var parseNumber = function parseNumber(value) {
|
||||
if (value.length !== 0) {
|
||||
var parsedNumber = Number(value.replace(/_/g, ''));
|
||||
if (!Number.isNaN(parsedNumber)) {
|
||||
return parsedNumber;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
var normalizeNumericChunk = function normalizeNumericChunk(chunk, index, chunks) {
|
||||
if (RE_INT_OR_FLOAT.test(chunk)) {
|
||||
// don´t parse a number, if there´s a preceding decimal point
|
||||
// to keep significance
|
||||
// e.g. 1.0020, 1.020
|
||||
if (!RE_LEADING_ZERO.test(chunk) || index === 0 || chunks[index - 1] !== '.') {
|
||||
return parseNumber(chunk) || 0;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
var createChunkMap = function createChunkMap(chunk, index, chunks) {
|
||||
return {
|
||||
parsedNumber: normalizeNumericChunk(chunk, index, chunks),
|
||||
normalizedString: normalizeAlphaChunk(chunk)
|
||||
};
|
||||
};
|
||||
|
||||
var createChunks = function createChunks(value) {
|
||||
return value.replace(RE_NUMBERS, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
|
||||
};
|
||||
|
||||
var createChunkMaps = function createChunkMaps(value) {
|
||||
var chunksMaps = createChunks(value).map(createChunkMap);
|
||||
return chunksMaps;
|
||||
};
|
||||
|
||||
var isFunction = function isFunction(value) {
|
||||
return typeof value === 'function';
|
||||
};
|
||||
|
||||
var isNaN = function isNaN(value) {
|
||||
return Number.isNaN(value) || value instanceof Number && Number.isNaN(value.valueOf());
|
||||
};
|
||||
|
||||
var isNull = function isNull(value) {
|
||||
return value === null;
|
||||
};
|
||||
|
||||
var isObject = function isObject(value) {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Number) && !(value instanceof String) && !(value instanceof Boolean) && !(value instanceof Date);
|
||||
};
|
||||
|
||||
var isSymbol = function isSymbol(value) {
|
||||
return typeof value === 'symbol';
|
||||
};
|
||||
|
||||
var isUndefined = function isUndefined(value) {
|
||||
return value === undefined;
|
||||
};
|
||||
|
||||
var parseDate = function parseDate(value) {
|
||||
try {
|
||||
var parsedDate = Date.parse(value);
|
||||
if (!Number.isNaN(parsedDate)) {
|
||||
if (RE_DATE.test(value)) {
|
||||
return parsedDate;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
} catch (_unused) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
var numberify = function numberify(value) {
|
||||
var parsedNumber = parseNumber(value);
|
||||
if (parsedNumber !== undefined) {
|
||||
return parsedNumber;
|
||||
}
|
||||
return parseDate(value);
|
||||
};
|
||||
|
||||
var stringify = function stringify(value) {
|
||||
if (typeof value === 'boolean' || value instanceof Boolean) {
|
||||
return Number(value).toString();
|
||||
}
|
||||
if (typeof value === 'number' || value instanceof Number) {
|
||||
return value.toString();
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return value.getTime().toString();
|
||||
}
|
||||
if (typeof value === 'string' || value instanceof String) {
|
||||
return value.toLowerCase().replace(RE_LEADING_OR_TRAILING_WHITESPACES, '');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
var getMappedValueRecord = function getMappedValueRecord(value) {
|
||||
if (typeof value === 'string' || value instanceof String || (typeof value === 'number' || value instanceof Number) && !isNaN(value) || typeof value === 'boolean' || value instanceof Boolean || value instanceof Date) {
|
||||
var stringValue = stringify(value);
|
||||
var parsedNumber = numberify(stringValue);
|
||||
var chunks = createChunkMaps(parsedNumber ? "" + parsedNumber : stringValue);
|
||||
return {
|
||||
parsedNumber: parsedNumber,
|
||||
chunks: chunks,
|
||||
value: value
|
||||
};
|
||||
}
|
||||
return {
|
||||
isArray: Array.isArray(value),
|
||||
isFunction: isFunction(value),
|
||||
isNaN: isNaN(value),
|
||||
isNull: isNull(value),
|
||||
isObject: isObject(value),
|
||||
isSymbol: isSymbol(value),
|
||||
isUndefined: isUndefined(value),
|
||||
value: value
|
||||
};
|
||||
};
|
||||
|
||||
var baseCompare = function baseCompare(options) {
|
||||
return function (valueA, valueB) {
|
||||
var a = getMappedValueRecord(valueA);
|
||||
var b = getMappedValueRecord(valueB);
|
||||
var result = compareValues(a, b, options.locale);
|
||||
return result * (options.order === 'desc' ? -1 : 1);
|
||||
};
|
||||
};
|
||||
|
||||
var isValidOrder = function isValidOrder(value) {
|
||||
return typeof value === 'string' && (value === 'asc' || value === 'desc');
|
||||
};
|
||||
var getOptions = function getOptions(customOptions) {
|
||||
var order = 'asc';
|
||||
var locale; // = 'en';
|
||||
if (typeof customOptions === 'string' && isValidOrder(customOptions)) {
|
||||
order = customOptions;
|
||||
} else if (customOptions && typeof customOptions === 'object') {
|
||||
if (customOptions.order && isValidOrder(customOptions.order)) {
|
||||
order = customOptions.order;
|
||||
}
|
||||
if (customOptions.locale && customOptions.locale.length > 0) {
|
||||
locale = customOptions.locale;
|
||||
}
|
||||
}
|
||||
return {
|
||||
order: order,
|
||||
locale: locale
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a compare function that defines the natural sort order considering
|
||||
* the given `options` which may be passed to [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
|
||||
*/
|
||||
function compare(options) {
|
||||
var validatedOptions = getOptions(options);
|
||||
return baseCompare(validatedOptions);
|
||||
}
|
||||
|
||||
var compareMultiple = function compareMultiple(recordA, recordB, orders, locale) {
|
||||
var indexA = recordA.index,
|
||||
valuesA = recordA.values;
|
||||
var indexB = recordB.index,
|
||||
valuesB = recordB.values;
|
||||
var length = valuesA.length;
|
||||
var ordersLength = orders.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
var order = i < ordersLength ? orders[i] : null;
|
||||
if (order && typeof order === 'function') {
|
||||
var result = order(valuesA[i].value, valuesB[i].value);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
var _result = compareValues(valuesA[i], valuesB[i], locale);
|
||||
if (_result) {
|
||||
return _result * (order === 'desc' ? -1 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return indexA - indexB;
|
||||
};
|
||||
|
||||
var createIdentifierFn = function createIdentifierFn(identifier) {
|
||||
if (typeof identifier === 'function') {
|
||||
// identifier is already a lookup function
|
||||
return identifier;
|
||||
}
|
||||
return function (value) {
|
||||
if (Array.isArray(value)) {
|
||||
var index = Number(identifier);
|
||||
if (Number.isInteger(index)) {
|
||||
return value[index];
|
||||
}
|
||||
} else if (value && typeof value === 'object') {
|
||||
var result = Object.getOwnPropertyDescriptor(value, identifier);
|
||||
return result == null ? void 0 : result.value;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
var getElementByIndex = function getElementByIndex(collection, index) {
|
||||
return collection[index];
|
||||
};
|
||||
|
||||
var getValueByIdentifier = function getValueByIdentifier(value, getValue) {
|
||||
return getValue(value);
|
||||
};
|
||||
|
||||
var baseOrderBy = function baseOrderBy(collection, identifiers, orders, locale) {
|
||||
var identifierFns = identifiers.length ? identifiers.map(createIdentifierFn) : [function (value) {
|
||||
return value;
|
||||
}];
|
||||
|
||||
// temporary array holds elements with position and sort-values
|
||||
var mappedCollection = collection.map(function (element, index) {
|
||||
var values = identifierFns.map(function (identifier) {
|
||||
return getValueByIdentifier(element, identifier);
|
||||
}).map(getMappedValueRecord);
|
||||
return {
|
||||
index: index,
|
||||
values: values
|
||||
};
|
||||
});
|
||||
|
||||
// iterate over values and compare values until a != b or last value reached
|
||||
mappedCollection.sort(function (recordA, recordB) {
|
||||
return compareMultiple(recordA, recordB, orders, locale);
|
||||
});
|
||||
return mappedCollection.map(function (element) {
|
||||
return getElementByIndex(collection, element.index);
|
||||
});
|
||||
};
|
||||
|
||||
var getIdentifiers = function getIdentifiers(identifiers) {
|
||||
if (!identifiers) {
|
||||
return [];
|
||||
}
|
||||
var identifierList = !Array.isArray(identifiers) ? [identifiers] : [].concat(identifiers);
|
||||
if (identifierList.some(function (identifier) {
|
||||
return typeof identifier !== 'string' && typeof identifier !== 'number' && typeof identifier !== 'function';
|
||||
})) {
|
||||
return [];
|
||||
}
|
||||
return identifierList;
|
||||
};
|
||||
|
||||
var getOrders = function getOrders(orders) {
|
||||
if (!orders) {
|
||||
return [];
|
||||
}
|
||||
var orderList = !Array.isArray(orders) ? [orders] : [].concat(orders);
|
||||
if (orderList.some(function (order) {
|
||||
return order !== 'asc' && order !== 'desc' && typeof order !== 'function';
|
||||
})) {
|
||||
return [];
|
||||
}
|
||||
return orderList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an array of elements, natural sorted by specified identifiers and
|
||||
* the corresponding sort orders. This method implements a stable sort
|
||||
* algorithm, which means the original sort order of equal elements is
|
||||
* preserved.
|
||||
*/
|
||||
function orderBy(collection, identifiers, orders, locale) {
|
||||
if (!collection || !Array.isArray(collection)) {
|
||||
return [];
|
||||
}
|
||||
var validatedIdentifiers = getIdentifiers(identifiers);
|
||||
var validatedOrders = getOrders(orders);
|
||||
return baseOrderBy(collection, validatedIdentifiers, validatedOrders, locale);
|
||||
}
|
||||
|
||||
exports.compare = compare;
|
||||
exports.orderBy = orderBy;
|
||||
|
||||
}));
|
||||
+11
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user