npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@shelf/fast-natural-order-by

v2.0.0

Published

Lightweight and performant natural sorting of arrays and collections by differentiating between unicode characters, numbers, dates, etc. 150,000x faster fork of natural-orderby for longer strings

Downloads

46,248

Readme

fast-natural-order-by CircleCI

Lightweight (< 2.3kB gzipped) and performant natural sorting of arrays and collections by differentiating between unicode characters, numbers, dates, etc.

Note: The permormance fixes & typescript were ported into natural-orderby >3.0.2, so >2 of this package just re-export the methods of natural-orderby & provide ESM only build A fork of natural-orderby that fixes performance when sorting strings longer than 20 characters (~150,000x faster). Also, re-written from Flow to Typescript

Install

$ yarn add @shelf/fast-natural-order-by

Intro

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.

@shelf/fast-natural-order-by sorts the primitive values of Boolean, Null, Undefined, Number or String type as well as 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 @shelf/fast-natural-order-by.

In addition to the efficient and fast orderBy() method @shelf/fast-natural-order-by also provides the method compare(), which may be passed to Array.prototype.sort().

Benchmark

This is a fork of natural-orderby that fixes performance when sorting strings longer than 20 characters (~150,000x faster).

Here are the benchmark results of sorting array with ONE SINGLE (1) element with a string 200 chars long that you can run by running yarn benchmark command:

  original:
    5 ops/s, ±0.29%         | slowest, 100% slower

  optimized:
    773 712 ops/s, ±0.24%   | fastest

See this commit to udnerstand how the performance was fixed.

Usage

import {orderBy} from '@shelf/fast-natural-order-by';

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():

[
  {
    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() invoking a compare function multiple times per element within the array.

Syntax

function orderBy<T>(
  collection: ReadonlyArray<T>,
  identifiers?: ReadonlyArray<Identifier<T>> | Identifier<T>,
  orders?: ReadonlyArray<Order> | Order
): Array<T>;

| Type | Value | | :-------------- | :----------------------------------------------------------------------------------- | | Identifier<T> | string | (value: T) => unknown) | | Order | 'asc' | 'desc' | (valueA: unknown, valueB: unknown) => number |

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 beexpressed 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.

Note: orderBy() always returns a new array, even if the original was already sorted.

Examples

import {orderBy} from '@shelf/fast-natural-order-by';

// 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().

Syntax

compare(options?: CompareOptions): CompareFn

| Type | Value | | :--------------- | :-------------------------------------------------------- | | CompareOptions | { order?: 'asc' | 'desc' } | | CompareFn | (valueA: unknown, valueB: unknown) => number |

Description

compare() returns a compare function that defines the natural sort order and which may be passed to Array.prototype.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.

Examples

import { compare } from '@shelf/fast-natural-order-by';

// 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'
//        },
//      },
//    ]

See Also

Credits

Publish

$ git checkout master
$ yarn version
$ yarn publish
$ git push origin master --tags

License

MIT © Shelf