Coder Social home page Coder Social logo

fast-sort's Introduction

fast-sort

Start Total Downloads Known Vulnerabilities Open Source Love MIT Licence

NPM Package

Fast-sort is a lightweight (850 bytes gzip), zero-dependency sorting library with TypeScript support. Its easy-to-use and flexible syntax, combined with incredible speed , make it a top choice for developers seeking efficient, reliable, and customizable sorting solutions.

Quick examples

  import { sort } from 'fast-sort';

  // Sort flat arrays
  const ascSorted = sort([1,4,2]).asc(); // => [1, 2, 4]
  const descSorted = sort([1, 4, 2]).desc(); // => [4, 2, 1]

  // Sort users (array of objects) by firstName in descending order
  const sorted = sort(users).desc(u => u.firstName);

  // Sort users in ascending order by firstName and lastName
  const sorted = sort(users).asc([
    u => u.firstName,
    u => u.lastName
  ]);

  // Sort users ascending by firstName and descending by city
  const sorted = sort(users).by([
    { asc: u => u.firstName },
    { desc: u => u.address.city }
  ]);

  // Sort based on computed property
  const sorted = sort(repositories).desc(r => r.openIssues + r.closedIssues);

  // Sort using string for object key
  // Only available for root object properties
  const sorted = sort(users).asc('firstName');

Fore more examples check unit tests.

In place sorting

Fast-sort provides an inPlace sorting option that mutates the original array instead of creating a new instance, resulting in marginally faster and more memory-efficient sorting. However, both the inPlaceSort and default sort methods offer exactly the same functionality.

const { sort, inPlaceSort } = require('fast-sort');

const array = [3, 1, 5];
const sorted = sort(array).asc();

// sorted => [1, 3, 5]
// array => [3, 1, 5]

inPlaceSort(array).asc();

// array => [1, 3, 5]

Natural sorting / Language sensitive sorting

By default fast-sort is not doing language sensitive sorting of strings. e.g 'image-11.jpg' will be sorted before 'image-2.jpg' (in ascending sorting). We can provide custom Intl.Collator comparer to fast-sort for language sensitive sorting of strings. Keep in mind that natural sort is slower then default sorting so recommendation is to use it only when needed.

  import { sort, createNewSortInstance } from 'fast-sort';

  const testArr = ['image-2.jpg', 'image-11.jpg', 'image-3.jpg'];

  // By default fast-sort is not doing natural sort
  sort(testArr).desc(); // => ['image-3.jpg', 'image-2.jpg', 'image-11.jpg']

  // We can use `by` sort to override default comparer
  // with the one that is doing language sensitive comparison
  sort(testArr).by({
    desc: true,
    comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare,
  }); // => ['image-11.jpg', 'image-3.jpg', 'image-2.jpg']

  // Or we can create new sort instance with language sensitive comparer.
  // Recommended if used in multiple places
  const naturalSort = createNewSortInstance({
    comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare,
  });

  naturalSort(testArr).asc(); // => ['image-2.jpg', 'image-3.jpg', 'image-11.jpg']
  naturalSort(testArr).desc(); // => ['image-11.jpg', 'image-3.jpg', 'image-2.jpg']

NOTE: It's known that Intl.Collator might not sort null values correctly so make sure to cast them to undefine as described in the following issue #54 (comment)

Custom sorting

Fast sort can be tailored to fit any sorting need or use case by:

  • creating custom sorting instances
  • overriding default comparer in by sorter
  • custom handling in provided callback function
  • combination of any from above

For example we will sort tags by "custom" tag importance (e.g vip tag is of greater importance then captain tag).

  import { sort, createNewSortInstance } from 'fast-sort';

  const tags = ['influencer', 'unknown', 'vip', 'captain'];
  const tagsImportance = { // Domain specific tag importance
    vip: 3,
    influencer: 2,
    captain: 1,
  };

  // We can use power of computed prop to sort tags by domain specific importance
  const descTags = sort(tags).desc(tag => tagImportance[tag] || 0);
  // => ['vip', 'influencer', 'captain', 'unknown'];

  // Or we can create specialized tagSorter so we can reuse it in multiple places
  const tagSorter = createNewSortInstance({
    comparer: (a, b) => (tagImportance[a] || 0) - (tagImportance[b] || 0),
    inPlaceSorting: true, // default[false] => Check "In Place Sort" section for more info.
  });

  tagSorter(tags).asc(); // => ['unknown', 'captain', 'influencer', 'vip'];
  tagSorter(tags).desc(); // => ['vip', 'influencer', 'captain', 'unknown'];

  // Default sorter will sort tags by comparing string values not by their domain specific value
  const defaultSort = sort(tags).asc(); // => ['captain', 'influencer', 'unknown' 'vip']

More examples

  // Sorting values that are not sortable will return same value back
  sort(null).asc(); // => null
  sort(33).desc(); // => 33

  // By default fast-sort sorts null and undefined values to the
  // bottom no matter if sorting is in asc or decs order.
  // If this is not intended behaviour you can check "Should create sort instance that sorts nil value to the top in desc order" test on how to override
  const addresses = [{ city: 'Split' }, { city: undefined }, { city: 'Zagreb'}];
  sort(addresses).asc(a => a.city); // => Split, Zagreb, undefined
  sort(addresses).desc(a => a.city); // => Zagreb, Split, undefined

Migrating from older versions

Documentation for v2 and older versions is available here.

For migrating to v3 you can reference CHANGELOG for what has been changed.

Benchmark

Five different benchmarks have been created to get better insight of how fast-sort perform under different scenarios. Each benchmark is run with different array sizes raging from small 100 items to large 100 000 items.

Every run of benchmark outputs different results but the results are constantly showing better scores compared to similar popular sorting libraries.

Benchmark scores

Benchmark has been run on:

  • 16 GB Ram
  • Intel® Core™ i5-4570 CPU @ 3.20GHz × 4
  • Ubuntu 16.04
  • Node 8.9.1

Independent benchmark results from MacBook Air can be found in following PR: #48

benchmark results

Running benchmark

To run benchmark on your PC follow steps from below

  1. git clone https://github.com/snovakovic/fast-sort.git
  2. cd fast-sort/benchmark
  3. npm install
  4. npm start

In case you notice any irregularities in benchmark or you want to add sort library to benchmark score please open issue here

fast-sort's People

Contributors

dependabot[bot] avatar linusu avatar mesqueeb avatar snovakovic avatar t3chguy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

fast-sort's Issues

How to sort something to the front every time?

The following custom sort always puts the "packs" schema at the end of the list.
Is there an easy way to make it always sort to the front?

inPlaceSort(filteredSchemas).asc((schema) => {
  const name = collectionConfig?.schemas?.[schema.name]?.name || schema.name;

  if (name === collectionConfig?.packsSchemaName || name === 'pack' || name === 'packs') {
    return 0; // 👈 this sorts to the end. Want to be in front.
  }

  return name;
});

Importing from ESM Node.js does not work

Importing this library from an ESM Node.js project ("type": "module" in the package.json) does not work, because the project is detected as CJS and the dist/sort.es.js-file is ignored.

The "module": "dist/sort.es.js" setting in the package.json only works for bundlers like webpack, but not for native Node.js ESM projects.

Even when pointing the import directly to the ESM file like import { sort } from "fast-sort/dist/sort.es.js" does not work, because Node.js expects this file to either end with .mjs or the fast-sort-project to be configured as ESM itself (i.e. having "type": "module in the package.json.

Adding an exports field to the package.json could solve this problem, by specifying the exact import locations to look for in case of ESM and CJS files. (https://nodejs.org/api/packages.html#package-entry-points).

how can I create ISortByObjectSorter<T>[] with inPlaceSorting=true?

I tried this:

export const SorterQualityNew: ISortByObjectSorter[] = [
{ desc: e => e.config.quality, inPlaceSorting: true },
{ asc: e => e.config.part, inPlaceSorting: true },
{ desc: e => e.lv, inPlaceSorting: true },
{ asc: e => e.config.id, inPlaceSorting: true },
];

But it didn't work, and I don't know how to create custom sorter that fits my needs.
Thank you advance!

Length check on by() is limiting

Hi,

The sort: Invalid usage of 'by' sorter... exception thrown when length is < 2 means that if the sort parameters are variable, the code around it has to change.

For example if my sort criteria is variable/dynamic, there may not always be multiple sort criteria and as such I need to change my code to accommodate this.

const criteria = [{ asc: 'name' }]
const sortable = sort([])
const [ firstDirection, firstProperty ] = Object.entries(criteria[0])[0]
const sorted = criteria.length < 2
  ? sortable[firstDirection](firstProperty)
  : sortable.by(criteria)

Which seems a bit convoluted and unnecessary.

Thoughts on removing the exception?

bug: does not sort by second sort function when the first function tries to sort dates that are equal

This test does not succeed : )

 it('Should sort dates correctly when the same dates', () => {
    const testArr = [
      { d: new Date(2000, 0, 1), n: 3 },
      { d: new Date(2000, 0, 1), n: 1 },
      { d: new Date(2000, 0, 1), n: 0 },
      { d: new Date(2000, 0, 1), n: 2 },
      { d: new Date(2000, 0, 1), n: 5 },
      { d: new Date(2000, 0, 1), n: 4 },
    ];
    const sorted = sort(testArr).asc([
      arr => arr.d,
      arr => arr.n,
    ]);
    assert.deepStrictEqual(sorted, [
      { d: new Date(2000, 0, 1), n: 0 },
      { d: new Date(2000, 0, 1), n: 1 },
      { d: new Date(2000, 0, 1), n: 2 },
      { d: new Date(2000, 0, 1), n: 3 },
      { d: new Date(2000, 0, 1), n: 4 },
      { d: new Date(2000, 0, 1), n: 5 },
    ]);
  });

@snovakovic can you fix this bug? It caused some issues for us when dates had the same values. :O

The expected behaviour is that when the dates are the same values, it should then sort by the second asc sort function, unless I misunderstood something.

image

TypeScript: Argument of type '"timestamp"' is not assignable to parameter

I have an array of objects:

[{ 
 message: "hello",
 timestamp: 1572525998959000
}, {
 message: "yes",
 timestamp: 1572525998960000
}]

I'm trying to sort it that way:

import sort from 'fast-sort'

const sortedMessages = sort(messages).desc("timestamp");
console.log(sortedMessages);

I get the following when running tsc:

src/api/api.ts:630:54 - error TS2345: Argument of type '"timestamp"' is not assignable to parameter of type 'ISortByFunction<unknown> | ISortByFunction<unknown>[] | ISortBy<unknown>[] | undefined'.

const sortedMessages = sort(messages).desc("timestamp");

Sort nested parameters within an array of objects within an array

I have the following app structure:

interface Partners {
	name: string;
	average_rating: number;
	apps: Array<{
		name: string;
		desc: string;
		rating: number;
		reviews: number;
	}>;
}

I need to sort based on average_rating first, then maximum number of reviews, then maximum number of rating & finally those containing maximum number of apps, i.e, apps.length.

So I made the following structure:

sort(partners).desc([
	(partner) => partner.average_rating,
	(partner) => partner.apps.reviews,
	(partner) => partner.apps.rating,
	(partner) => partner.apps.length,
])

But this doesn't seem to get me desired results. As multiple apps have average_rating = 5 which is the highest, they all rank on that.

reviews is never tracked. Do I have to use Array.reduce there to accumulate sum? Same goes for rating.

average_rating & apps.length are simpler but reviews & rating are a bit difficult as they have array of objects.

Basically I want to select all items with higher average_rating & lots of reviews then sorted by rating & finally by number of apps they have :)

Undefined, null, and empty string values aren't getting sorted properly when using Intl.Collator().

I am not sure if this is intended but I came across an issue when trying to create a natural sort algorithm and noticed that the undefined, null, and empty string values get sorted in random orders depending on which value was used. This seems to only be happening with array of objects as far as I know.

I have a CodeSandbox here: https://codesandbox.io/s/fast-sort-issues-4fdkcw?file=/src/App.tsx to kinda show what's going on.

1.) If some of the data has empty strings for the name field then the empty string will be placed at the top or at the bottom depending on sort order. Not the biggest concern because I think this is by design.

2.) If some of the data has undefined or null values then the items will be semi-sorted in the list. The undefined/null values will sometimes be in the middle, on top, on bottom, really close to the bottom, etc. This is the main concern.

incorrect sorting

image
(using default [].sort())

image
using fast-sort

it provides different values to normal sorting.

image
another example ^

yea its for being used on a discord bot and it's a lot faster, but if its not accurate unfortunately i cant use it. any way for me improve the accuracy?

How to support utf-8 chars in german

Great library, i wunder how to add custom sort order for strings. In german language there are some characters existing like Ä, Ö, Ü which normaly are sorted like A, Ö, Ü.

issue facing when upgrading angular cli version 1.2.6 to 1.7.4

Image.

I am struck with the issue

ERROR TypeError: fast_sort_1.default is not a function

This function was working correctly till angular cli version 1.2.6 but when i upgraded to version 1.7.4 , I am getting above error.

Please let me now If you need any additional information

Sorting with default Values

I am currently using the following approach:

dates: sort(listOfDates).by([
        { desc: 'isMain' },
        { desc: 'unreadMessagesCount' },
        { desc: 'lastAccess' }
      ]),

However sometimes I might not have some of these parameters, is there a way to sort by default?

3.1.3 version disappeared

Where is 3.1.3 version?

My yarn.lock with 3.1.3 version on May 24th, 2022 11:43 AM.
image

The releases available today:
image

sorting order changes between redundant values

let suppose arr = [ {name:'xyz',id:1},{name:"xyz",id:2},{name:'abc',id:3}]
if we rerender the sorting order of xyz changes every time like before render its at first place then after render on second
but if xyz value donot have duplicate it works fine
arr = [ {name:'xyz',id:1},{name:"rst",id:2},{name:'abc',id:3}]
thank you

Bug: sort by comparer overrides the default sort

It appears that if you are using sort.by() with an array, custom comparers are not working as expected.

See this minimal test case.

In that example I'm sorting by status and then by title. Status has a custom comparer and title does not. However, you can see in the logs that the status comparer is being called for the title sort as well. It does not fall back to the default once a comparer has been given.

I would expect the test case to output

[
{ status: 0, title: "A" },
{ status: 0, title: "B" },
{ status: 0, title: "D" },
{ status: 1 title: "C" },
]

However it outputs

[
{ status: 0, title: "A" },
{ status: 0, title: "D" },
{ status: 0, title: "B" },
{ status: 1 title: "C" },
]

If I remove the numeric comparer from status, or add the alpha comparer to title, or if I remove the numeric comparer from status and add the alpha comparer to title then it outputs as expected.

do your tests still pass?

I merged your master into my forks master and 2 tests failed. : S

image

do your tests still pass on v3?

Is this project dead?

We recently picked this as our sort library, but it seems that issues/PRs are no longer being read/picked up.

I'm not asking for anything, just want to check whether this is alive or dead. In the latter case, we would try to find a replacement or run our own fork of this.

Sorting combination of alphabets and numbers

Hello,
So I am trying to sort a combination of alphabets and integers.
When I execute the following sort(["Abcd 1", "Abcd 2", "Abcd 11", "Abcd 33", "Abcd 4"]).asc()
The result get is  ["Abcd 1", "Abcd 11", "Abcd 2", "Abcd 33", "Abcd 4"]

Shouldn't it be ["Abcd 1","Abcd 2","Abcd 11","Abcd 33","Abcd 4"] ?

TypeError fast_sort_1.default is not a function

I get this error when trying to import it in a typescript file (with ts-node) for a test to run with nodeJS.
import sort from 'fast-sort'

TypeError {
message: 'fast_sort_1.default is not a function',
}

I had a similar problem with one of my own libraries that had default exports.
I was able to fix it by only providing named exports instead of "default"

But this would be a breaking change so would require a major version bump.

PS: I'm on node v12.16.2

TypeError fast_sort_1.default is not a function (proper fix)

When using ts-node this error appears:

TypeError fast_sort_1.default is not a function

I know there was this #22 already closed issue, but the proposed solution there (adding "esModuleInterop": true) breaks other imports like koa.js, it's not the best solution, what worked for me was this:

Replace this import:
import sort from 'fast-sort';

by this one:
import * as sort from 'fast-sort';

The library will start working, but typescript won't compile, a workaround for the typescript issue is this:

Create a file and add this:

import * as _sort from 'fast-sort';
export const sort = _sort as unknown as typeof _sort.default;

then import sort from your file instead of importing it from 'fast-sort' module

This typescript workaround can be avoided if you implement a fix in sort.d.ts file

version 3.0 wishes

I love this amazing library. it's by far my favourite on whole github!!

I'd love in version 3.0 that the library by default does not mutate arrays.

This is better to promote "pure functions" as a convention.

eg.

const mySortedArray = sort(myArray).asc(e => e.someField)

I think default behaviour is much better if the above does not mutate myArray but the result is just returned and needs to be saved in a new variable.

Typescript issue "Argument of type is not assignable"

Hi, really excited to find this great library! The code is working, but my editor is giving the following Typescript error.

Argument of type '"created_at"' is not assignable to parameter of type 'ISortByFunction<unknown> | ISortByFunction<unknown>[] | ISortBy<unknown>[]'.

const entries = [
{ id: 1, name: 'Project 1', created_at: '2020-04-23T01:30:59.657Z'},
{ id: 2, name: 'Project 2', created_at: '2020-04-23T01:30:58.657Z'},
{ id: 3, name: 'Project 3', created_at: '2020-04-23T01:30:57.657Z'}
];

const projects = sort(entries).desc('created_at');

Any help would be greatly appreciated, i'm pretty new to Typescript, not sure if it's something im doing wrong, or if I found a bug. Thanks!

Combing naturalSort and sort

Hi and thanks for this very useful library,

We are using the library to sort multiple prioritized columns in a datable.
This means that the user can sort Column A ascending and afterwards Column B descending with respect to the sorted data in Column A, like below:

A 1
A 2
A 3
B 1
B 2
B 3

We use naturalSort and the by() function to do so.

const naturalSort = createNewSortInstance({
  comparer: new Intl.Collator(locale, { numeric: true, sensitivity: 'base' }).compare,
});
const sortedQueue = naturalSort(queue).by(sortObjects)

We're using natural sort since we want e.g. House 10 to be sorted after House 2 and not between House 1 and House 2.

Due to the way naturalSort works it is not fit for negative numbers, since everything is sorted as strings, resulting in that the array [-10, -15, -1, 0, 1 ,10, 15] will be sorted as [-1, -10, -15, 0, 1, 10, 15].

This can, of course, be solved by using the sort() function from fast-sort but this breaks the previously described behaviour wherein we need to sort strings correctly with natural sort.

Ideally this should be the result when sorting multiple columns containing strings and numbers:

Name   Temperature
A1         -10    
A2         -5
A10       -1
B2         0
B2         1
B3         2

So my question is; can fast-sort handle our use-case e.g. sort one column as integers and afterwards sort another column with naturalSort and with respect to the first column and in that case, how?

Thanks.

Support Natural Sort

Hi,

May i know fast-sort support natural sort

fastSort([{"companyId":"A","siteId":"5"},{"companyId":"A","siteId":"21"},{"companyId":"A","siteId":"1"}]).asc([
    'siteId'
  ]);

Its return

0: Object {companyId: "A", siteId: "1"}
1: Object {companyId: "A", siteId: "21"}
2: Object {companyId: "A", siteId: "5"}

instead of

0: Object {companyId: "A", siteId: "1"}
1: Object {companyId: "A", siteId: "5"}
2: Object {companyId: "A", siteId: "21"}

[Feature Request] Sorting ascending / descending on multiple properties in array.

First of all, stumbled across this library very pleased with the speed in testing and comparing it to other sorting algorithms. I am using it in my library for grid sorting (unfortunately its ran in a web worker so this won't show up as a dependency in the project).

Anyways, it would be nice if this supported the ability to sort by properties in 'asc' or 'desc'.

  sort(persons).desc([
    (p) => p.firstName, // Sort by first name
    (p) => p.lastName, // Persons that have same firstName will be sorted by lastName
    (p) => p.dob // Persons that have same firstName and lastName will be sorted by dob
  ]);

This is great but it would be awesome if this library supported something more like...

  sort(persons, [
    {'firstName', 'asc'},
    {'dob', 'desc'}
    ]
  ]);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.