routie dev init since i didn't adhere to any proper guidance up until now

This commit is contained in:
2026-04-29 22:27:29 -06:00
commit e1dabb71e2
15301 changed files with 3562618 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
# natural-orderby
## 5.0.0
### Major Changes
- feat: support numeric separators
- feat: add ability to define locale for sorting unicode
## 4.0.0
### Major Changes
- fix: update `Identifier<T>` to be `keyof T` instead of any `string`
## 3.0.2
### Patch Changes
- fix: performance issue when parsing date strings
## 3.0.1
### Patch Changes
- fix umd imports
## 3.0.0
### Major Changes
- Migrate codebase to TypeScript
+9
View File
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2018 - present Olaf Ennen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+538
View File
@@ -0,0 +1,538 @@
# Welcome to 🌲 natural-orderby
Lightweight (< 1.6kB gzipped) and performant natural sorting of arrays and collections by differentiating between unicode characters, numbers, dates, etc.
[![npm version](https://img.shields.io/npm/v/natural-orderby.svg)](https://www.npmjs.com/package/natural-orderby)
[![downloads](https://img.shields.io/npm/dm/natural-orderby.svg)](https://www.npmjs.com/package/natural-orderby)
[![CI](https://github.com/yobacca/natural-orderby/workflows/CI/badge.svg)](https://github.com/yobacca/natural-orderby/actions)
[![coverage](https://img.shields.io/codecov/c/github/yobacca/natural-orderby.svg)](https://codecov.io/gh/yobacca/natural-orderby)
People sort strings containing numbers differently than most sorting algorithms, which sort values by comparing strings in Unicode code point order. This produces an ordering that is inconsistent with human logic.
`natural-orderby` sorts the primitive values of [`Boolean`](https://developer.mozilla.org/en-US/docs/Glossary/Boolean), [`Null`](https://developer.mozilla.org/en-US/docs/Glossary/Null), [`Undefined`](https://developer.mozilla.org/en-US/docs/Glossary/Undefined), [`Number`](https://developer.mozilla.org/en-US/docs/Glossary/Number) or [`String`](https://developer.mozilla.org/en-US/docs/Glossary/String) type as well as [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) objects. When comparing strings it differentiates between unicode characters, integer, floating as well as hexadecimal numbers, various date formats, etc. You may sort flat or nested arrays or arrays of objects in a natural sorting order using `natural-orderby`.
In addition to the efficient and fast `orderBy()` method `natural-orderby` also provides the method `compare()`, which may be passed to [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
## Contents
- [Getting Started](#getting-started)
- [Usage](#usage)
- [API Reference](#api-reference)
- [TypeScript Declarations](#typescript-declarations)
- [Credits](#credits)
- [License](#license)
---
## Getting Started
```bash
# npm
npm install natural-orderby --save
# yarn
yarn add natural-orderby
```
If you´re not using a module bundler or package manager there´s also a global ("IIFE") build hosted on the unpkg CDN. Simply add the following `<script>` tag to the bottom of your HTML file:
```html
<script src="https://unpkg.com/natural-orderby/dist/umd/natural-orderby.production.min.js"></script>
```
Once you've added `natural-orderby` you will have access to the global `window.naturalOrderBy` variable.
## Usage
<!-- prettier-ignore -->
```javascript
// Using ES modules
import { orderBy } from 'natural-orderby';
// Using CommonJS modules
// const { orderBy } = require('natural-orderby');
const users = [
{
username: 'Bamm-Bamm',
ip: '192.168.5.2',
datetime: 'Fri Jun 15 2018 16:48:00 GMT+0200 (CEST)'
},
{
username: 'Wilma',
ip: '192.168.10.1',
datetime: '14 Jun 2018 00:00:00 PDT'
},
{
username: 'Dino',
ip: '192.168.0.2',
datetime: 'June 15, 2018 14:48:00'
},
{
username: 'Barney',
ip: '192.168.1.1',
datetime: 'Thu, 14 Jun 2018 07:00:00 GMT'
},
{
username: 'Pebbles',
ip: '192.168.1.21',
datetime: '15 June 2018 14:48 UTC'
},
{
username: 'Hoppy',
ip: '192.168.5.10',
datetime: '2018-06-15T14:48:00.000Z'
},
];
const sortedUsers = orderBy(
users,
[v => v.datetime, v => v.ip],
['desc', 'asc']
);
```
This is the return value of `orderBy()`:
```javascript
[
{
username: 'Dino',
ip: '192.168.0.2',
datetime: 'June 15, 2018 14:48:00',
},
{
username: 'Pebbles',
ip: '192.168.1.21',
datetime: '15 June 2018 14:48 UTC',
},
{
username: 'Bamm-Bamm',
ip: '192.168.5.2',
datetime: 'Fri Jun 15 2018 16:48:00 GMT+0200 (CEST)',
},
{
username: 'Hoppy',
ip: '192.168.5.10',
datetime: '2018-06-15T14:48:00.000Z',
},
{
username: 'Barney',
ip: '192.168.1.1',
datetime: 'Thu, 14 Jun 2018 07:00:00 GMT',
},
{
username: 'Wilma',
ip: '192.168.10.1',
datetime: '14 Jun 2018 00:00:00 PDT',
},
];
```
## API Reference
### `orderBy()`
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.
It also avoids the high overhead caused by [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) invoking a compare function multiple times per element within the array.
#### Syntax
<!-- prettier-ignore -->
```typescript
orderBy<T>(
collection: ReadonlyArray<T>,
identifiers?: ReadonlyArray<Identifier<T>> | Identifier<T> | null,
orders?: ReadonlyArray<Order> | Order | null
locale?: string
): Array<T>
```
| Type | Value |
| :-------------- | :----------------------------------------------------------------------------------- |
| `Identifier<T>` | <code>keyof T &#124; number &#124; (value: T) => unknown</code> |
| `Order` | <code>'asc' &#124; 'desc' &#124; (valueA: unknown, valueB: unknown) => number</code> |
#### Description
`orderBy()` sorts the elements of an array by specified identifiers and the corresponding sort orders in a natural order and returns a new array containing the sorted elements.
If `collection` is an array of primitives, `identifiers` may be unspecified. Otherwise, you should specify `identifiers` to sort by or `collection` will be returned unsorted. An identifier can be expressed by:
- an index position, if `collection` is a nested array,
- a property name, if `collection` is an array of objects,
- a function which returns a particular value from an element of a nested array or an array of objects. This function will be invoked by passing one element of `collection`.
If `orders` is unspecified, all values are sorted in ascending order. Otherwise, specify an order of `'desc'` for descending or `'asc'` for ascending sort order of corresponding values. You may also specify a compare function for an order, which will be invoked by two arguments: `(valueA, valueB)`. It must return a number representing the sort order.
If you want to sort unicode strings according to a specific locale, please provide a string value for `locale` with a BCP 47 language tag. If the argument is unspecified, the host locale setting will be used.
> Note: `orderBy()` always returns a new array, even if the original was already sorted.
#### Examples
<!-- prettier-ignore -->
```javascript
import { orderBy } from 'natural-orderby';
// Simple numerics
orderBy(['10', 9, 2, '1', '4']);
// => ['1', 2, '4', 9, '10']
// Floats
orderBy(['10.0401', 10.022, 10.042, '10.021999']);
// => ['10.021999', 10.022, '10.0401', 10.042]
// Float & decimal notation
orderBy(['10.04f', '10.039F', '10.038d', '10.037D']);
// => ['10.037D', '10.038d', '10.039F', '10.04f']
// Scientific notation
orderBy(['1.528535047e5', '1.528535047e7', '1.528535047e3']);
// => ['1.528535047e3', '1.528535047e5', '1.528535047e7']
// IP addresses
orderBy(['192.168.201.100', '192.168.201.12', '192.168.21.1']);
// => ['192.168.21.1', '192.168.201.12', '192.168.21.100']
// Filenames
orderBy(['01asset_0815.png', 'asset_47103.jpg', 'asset_151.jpg', '001asset_4711.jpg', 'asset_342.mp4']);
// => ['001asset_4711.jpg', '01asset_0815.png', 'asset_151.jpg', 'asset_342.mp4', 'asset_47103.jpg']
// Filenames - ordered by extension and filename
orderBy(
['01asset_0815.png', 'asset_47103.jpg', 'asset_151.jpg', '001asset_4711.jpg', 'asset_342.mp4'],[v => v.split('.').pop(), v => v]
);
// => ['001asset_4711.jpg', 'asset_151.jpg', 'asset_47103.jpg', 'asset_342.mp4', '01asset_0815.png']
// Dates
orderBy(['10/12/2018', '10/11/2018', '10/11/2017', '10/12/2017']);
// => ['10/11/2017', '10/12/2017', '10/11/2018', '10/12/2018']
orderBy(['Thu, 15 Jun 2017 20:45:30 GMT', 'Thu, 3 May 2018 17:45:30 GMT', 'Thu, 15 Jun 2017 17:45:30 GMT']);
// => ['Thu, 15 Jun 2017 17:45:30 GMT', 'Thu, 15 Jun 2018 20:45:30 GMT', 'Thu, 3 May 2018 17:45:30 GMT']
// Money
orderBy(['$102.00', '$21.10', '$101.02', '$101.01']);
// => ['$21.10', '$101.01', '$101.02', '$102.00']
// Case-insensitive sort order
orderBy(['A', 'C', 'E', 'b', 'd', 'f']);
// => ['A', 'b', 'C', 'd', 'E', 'f']
// Default ascending sort order
orderBy(['a', 'c', 'f', 'd', 'e', 'b']);
// => ['a', 'b', 'c', 'd', 'e', 'f']
// Descending sort order
orderBy(['a', 'c', 'f', 'd', 'e', 'b'], null, ['desc']);
// => ['f', 'e', 'd', 'c', 'b', 'a']
// Custom compare function
orderBy([2, 1, 5, 8, 6, 9], null, [(valueA, valueB) => valueA - valueB]);
// => [1, 2, 5, 6, 8, 9]
// collections
const users = [
{
username: 'Bamm-Bamm',
ip: '192.168.5.2',
datetime: 'Fri Jun 15 2018 16:48:00 GMT+0200 (CEST)'
},
{
username: 'Wilma',
ip: '192.168.10.1',
datetime: '14 Jun 2018 00:00:00 PDT'
},
{
username: 'Dino',
ip: '192.168.0.2',
datetime: 'June 15, 2018 14:48:00'
},
{
username: 'Barney',
ip: '192.168.1.1',
datetime: 'Thu, 14 Jun 2018 07:00:00 GMT'
},
{
username: 'Pebbles',
ip: '192.168.1.21',
datetime: '15 June 2018 14:48 UTC'
},
{
username: 'Hoppy',
ip: '192.168.5.10',
datetime: '2018-06-15T14:48:00.000Z'
},
];
orderBy(
users,
[v => v.datetime, v => v.ip],
['desc', 'asc']
);
// => [
// {
// username: 'Dino',
// ip: '192.168.0.2',
// datetime: 'June 15, 2018 14:48:00',
// },
// {
// username: 'Pebbles',
// ip: '192.168.1.21',
// datetime: '15 June 2018 14:48 UTC',
// },
// {
// username: 'Bamm-Bamm',
// ip: '192.168.5.2',
// datetime: 'Fri Jun 15 2018 16:48:00 GMT+0200 (CEST)',
// },
// {
// username: 'Hoppy',
// ip: '192.168.5.10',
// datetime: '2018-06-15T14:48:00.000Z',
// },
// {
// username: 'Barney',
// ip: '192.168.1.1',
// datetime: 'Thu, 14 Jun 2018 07:00:00 GMT',
// },
// {
// username: 'Wilma',
// ip: '192.168.10.1',
// datetime: '14 Jun 2018 00:00:00 PDT',
// },
// ]
```
### `compare()`
Creates a compare function that defines the natural sort order and which may be passed to [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
#### Syntax
<!-- prettier-ignore -->
```javascript
compare(options?: CompareOptions): CompareFn
```
| Type | Value |
| :--------------- | :------------------------------------------------------------ |
| `CompareOptions` | <code>{ order?: 'asc' &#124; 'desc', locale?: string }</code> |
| `CompareFn` | <code>(valueA: unknown, valueB: unknown) => number</code> |
#### Description
`compare()` returns a compare function that defines the natural sort order and which may be passed to [`Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
If `options` or its property `order` is unspecified, values are sorted in ascending sort order. Otherwise, specify an order of `'desc'` for descending or `'asc'` for ascending sort order of values.
If unicode strings should be ordered corresponding to a specific locale setting, specify the according value for the options property `locale`. It must be a string with a BCP 47 language tag. If the argument is unspecified, the host locale setting will be used.
#### Examples
<!-- prettier-ignore -->
```javascript
import { compare } from 'natural-orderby';
// Simple numerics
['10', 9, 2, '1', '4'].sort(compare());
// => ['1', 2, '4', 9, '10']
// Floats
['10.0401', 10.022, 10.042, '10.021999'].sort(compare());
// => ['10.021999', 10.022, '10.0401', 10.042]
// Float & decimal notation
['10.04f', '10.039F', '10.038d', '10.037D'].sort(compare());
// => ['10.037D', '10.038d', '10.039F', '10.04f']
// Scientific notation
['1.528535047e5', '1.528535047e7', '1.528535047e3'].sort(compare());
// => ['1.528535047e3', '1.528535047e5', '1.528535047e7']
// IP addresses
['192.168.201.100', '192.168.201.12', '192.168.21.1'].sort(compare());
// => ['192.168.21.1', '192.168.201.12', '192.168.21.100']
// Filenames
['01asset_0815.jpg', 'asset_47103.jpg', 'asset_151.jpg', '001asset_4711.jpg', 'asset_342.mp4'].sort(compare());
// => ['001asset_4711.jpg', '01asset_0815.jpg', 'asset_151.jpg', 'asset_342.mp4', 'asset_47103.jpg']
// Dates
['10/12/2018', '10/11/2018', '10/11/2017', '10/12/2017'].sort(compare());
// => ['10/11/2017', '10/12/2017', '10/11/2018', '10/12/2018']
['Thu, 15 Jun 2017 20:45:30 GMT', 'Thu, 3 May 2018 17:45:30 GMT', 'Thu, 15 Jun 2017 17:45:30 GMT'].sort(compare());
// => ['Thu, 15 Jun 2017 17:45:30 GMT', 'Thu, 15 Jun 2018 20:45:30 GMT', 'Thu, 3 May 2018 17:45:30 GMT']
// Money
['$102.00', '$21.10', '$101.02', '$101.01'].sort(compare());
// => ['$21.10', '$101.01', '$101.02', '$102.00']
// Case-insensitive sort order
['A', 'C', 'E', 'b', 'd', 'f'].sort(compare());
// => ['A', 'b', 'C', 'd', 'E', 'f']
// Default ascending sort order
['a', 'c', 'f', 'd', 'e', 'b'].sort(compare());
// => ['a', 'b', 'c', 'd', 'e', 'f']
// Descending sort order
['a', 'c', 'f', 'd', 'e', 'b'].sort(compare({ order: 'desc' }));
// => ['f', 'e', 'd', 'c', 'b', 'a']
// collections
const users = [
{
username: 'Bamm-Bamm',
lastLogin: {
ip: '192.168.5.2',
datetime: 'Fri Jun 15 2018 16:48:00 GMT+0200 (CEST)'
},
},
{
username: 'Wilma',
lastLogin: {
ip: '192.168.10.1',
datetime: '14 Jun 2018 00:00:00 PDT'
},
},
{
username: 'Dino',
lastLogin: {
ip: '192.168.0.2',
datetime: 'June 15, 2018 14:48:00'
},
},
{
username: 'Barney',
lastLogin: {
ip: '192.168.1.1',
datetime: 'Thu, 14 Jun 2018 07:00:00 GMT'
},
},
{
username: 'Pebbles',
lastLogin: {
ip: '192.168.1.21',
datetime: '15 June 2018 14:48 UTC'
},
},
{
username: 'Hoppy',
lastLogin: {
ip: '192.168.5.10',
datetime: '2018-06-15T14:48:00.000Z'
},
},
];
users.sort((a, b) => compare()(a.lastLogin.ip, b.lastLogin.ip));
// => [
// {
// username: 'Dino',
// lastLogin: {
// ip: '192.168.0.2',
// datetime: 'June 15, 2018 14:48:00'
// },
// },
// {
// username: 'Barney',
// lastLogin: {
// ip: '192.168.1.1',
// datetime: 'Thu, 14 Jun 2018 07:00:00 GMT'
// },
// },
// {
// username: 'Pebbles',
// lastLogin: {
// ip: '192.168.1.21',
// datetime: '15 June 2018 14:48 UTC'
// },
// },
// {
// username: 'Bamm-Bamm',
// lastLogin: {
// ip: '192.168.5.2',
// datetime: 'Fri Jun 15 2018 16:48:00 GMT+0200 (CEST)'
// },
// },
// {
// username: 'Hoppy',
// lastLogin: {
// ip: '192.168.5.10',
// datetime: '2018-06-15T14:48:00.000Z'
// },
// },
// {
// username: 'Wilma',
// lastLogin: {
// ip: '192.168.10.1',
// datetime: '14 Jun 2018 00:00:00 PDT'
// },
// },
// ]
```
## TypeScript Declarations
`natural-orderby` is completely written in [TypeScript](https://www.typescriptlang.org/) and provides TypeScript declarations.
## Credits
Inspired by [The Alphanum Algorithm](http://www.davekoelle.com/alphanum.html) from Dave Koelle.
## License
Licensed under the MIT License, Copyright © 2018 - present Olaf Ennen.
See [LICENSE](./LICENSE.md) for more information.
+26
View File
@@ -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
View File
@@ -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
View File
@@ -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');
}
@@ -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 };
@@ -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};
@@ -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;
}));
File diff suppressed because one or more lines are too long
+119
View File
@@ -0,0 +1,119 @@
{
"name": "natural-orderby",
"version": "5.0.0",
"description": "Lightweight and performant natural sorting of arrays and collections by differentiating between unicode characters, numbers, dates, etc.",
"sideEffects": false,
"main": "./dist/main.js",
"types": "./dist/index.d.ts",
"module": "./dist/index.js",
"repository": {
"type": "git",
"url": "https://github.com/yobacca/natural-orderby.git"
},
"scripts": {
"build": "tsc && rollup -c",
"clean": "git clean -fdX .",
"docs": "docsify serve docs",
"format": "prettier --ignore-path .eslintignore --write src",
"lint": "eslint src",
"test": "cross-env TZ=UTC jest src",
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
"size": "filesize",
"prepublishOnly": "yarn build",
"prepare": "husky"
},
"files": [
"dist/",
"LICENSE.md",
"CHANGELOG.md",
"README.md"
],
"keywords": [
"sort",
"order",
"string",
"natsort",
"natcmp",
"compare",
"alphanum",
"unicode",
"date",
"number",
"float",
"numeric",
"natural",
"human",
"javascript",
"array",
"collection",
"list",
"sorting",
"case sensitive",
"case insensitive",
"ascending",
"descending",
"browser",
"node.js",
"node"
],
"author": "Olaf Ennen <olaf.ennen@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/yobacca/natural-orderby/issues"
},
"homepage": "https://yobacca.github.io/natural-orderby",
"dependencies": {},
"devDependencies": {
"@ampproject/filesize": "^4.3.0",
"@babel/core": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/preset-modules": "^0.1.5",
"@babel/preset-typescript": "^7.17.12",
"@changesets/cli": "^2.25.2",
"@commitlint/cli": "^19.4.0",
"@commitlint/config-conventional": "^19.2.2",
"@commitlint/types": "^19.0.3",
"@rollup/plugin-babel": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-terser": "^0.4.4",
"@types/jest": "29.5.13",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"cross-env": "^7.0.3",
"docsify-cli": "^4.3.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^28.8.0",
"eslint-plugin-n": "^17.10.2",
"eslint-plugin-prettier": "^5.2.1",
"husky": "^9.1.5",
"jest": "^29.3.1",
"lint-staged": "^15.2.9",
"prettier": "^3.3.3",
"rollup": "^4.21.1",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-dts": "^6.1.1",
"ts-jest": "^29.0.3",
"typescript": "^5.5.4"
},
"lint-staged": {
"*.{ts,md,json}": [
"prettier --write"
],
"*.{ts,json}": [
"eslint --rule \"@typescript-eslint/no-unused-vars: error\""
]
},
"engines": {
"node": ">=18"
},
"filesize": {
"./dist/umd/natural-orderby.production.min.js": {
"none": "5.5 kB"
},
"./dist/natural-orderby.production.min.js": {
"none": "4.6 kB"
}
}
}