{"id":27774285,"url":"https://github.com/lightness/type-comparator","last_synced_at":"2025-04-30T02:05:28.003Z","repository":{"id":57383170,"uuid":"170720497","full_name":"lightness/type-comparator","owner":"lightness","description":"Useful comparator functions written on Typescript","archived":false,"fork":false,"pushed_at":"2023-02-23T18:41:12.000Z","size":1162,"stargazers_count":53,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-30T02:05:22.736Z","etag":null,"topics":["comparator","javascript","ordering","sorting","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lightness.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-14T16:16:38.000Z","updated_at":"2024-07-12T19:01:17.000Z","dependencies_parsed_at":"2022-09-13T23:50:54.766Z","dependency_job_id":null,"html_url":"https://github.com/lightness/type-comparator","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightness%2Ftype-comparator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightness%2Ftype-comparator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightness%2Ftype-comparator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightness%2Ftype-comparator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lightness","download_url":"https://codeload.github.com/lightness/type-comparator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251626895,"owners_count":21617743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["comparator","javascript","ordering","sorting","typescript"],"created_at":"2025-04-30T02:05:27.332Z","updated_at":"2025-04-30T02:05:27.997Z","avatar_url":"https://github.com/lightness.png","language":"TypeScript","readme":"\n\u003cimg src=\"https://pp.userapi.com/c852036/v852036486/bbce5/DCotw_jzAKo.jpg\" width=\"100\" alt=\"Logo\" align=\"right\" /\u003e\n\n\u003ca href=\"https://www.npmjs.com/package/type-comparator\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/type-comparator.svg?style=flat\" alt=\"NPM Version\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/type-comparator\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/l/type-comparator.svg?style=flat\" alt=\"Package License\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/type-comparator\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/type-comparator.svg?style=flat\" alt=\"NPM Downloads\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://travis-ci.org/lightness/type-comparator\"\u003e\n    \u003cimg src=\"https://img.shields.io/travis/lightness/type-comparator.svg?style=flat\" alt=\"Travis\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/lightness/type-comparator\"\u003e\n    \u003cimg src=\"https://img.shields.io/codecov/c/github/lightness/type-comparator.svg?style=flat\" alt=\"Travis\" /\u003e\n\u003c/a\u003e\n\n# Type Comparator\n\nUseful comparator functions written on Typescript (But you can use it on your JS project)\n\n\u003cimg src=\"https://pp.userapi.com/c847124/v847124060/1bc6cf/t_jixj5HLK4.jpg\" width=\"730\" alt=\"Image\" align=\"center\" /\u003e\n\n## Table of Contents\n\n- [Type Comparator](#type-comparator)\n  - [Table of Contents](#table-of-contents)\n  - [Installation](#installation)\n  - [Usage](#usage)\n    - [Base comparators: `asc` and `desc`](#base-comparators-asc-and-desc)\n    - [Functional way](#functional-way)\n      - [Function `reverse(comparator)`](#function-reversecomparator)\n      - [Function `map(mapper, comparator)`](#function-mapmapper-comparator)\n      - [Function `condition(conditionFn, comparatorA, comparatorB)`](#function-conditionconditionfn-comparatora-comparatorb)\n      - [Function `queue(comparators)`](#function-queuecomparators)\n    - [Chaining way](#chaining-way)\n      - [Basic usage](#basic-usage)\n      - [Chain `.reverse()`](#chain-reverse)\n      - [Chain `.map(mapper)`](#chain-mapmapper)\n      - [Chain `.if(conditionFn)`](#chain-ifconditionfn)\n      - [Chain `.then(comparator)`](#chain-thencomparator)\n      - [Chain `.elif(conditionFn)`](#chain-elifconditionfn)\n      - [Chain `.else(comparator)`](#chain-elsecomparator)\n      - [Chain `.use(comparators)`](#chain-usecomparators)\n    - [Something more complex?... Ok!](#something-more-complex-ok)\n    - [Q\u0026A](#qa)\n  - [Support](#support)\n  - [Contributing](#contributing)\n\n## Installation\n\n```sh\nnpm i type-comparator\n```\n\n## Usage\n\n### Base comparators: `asc` and `desc` \n\n`asc` is simple comparator contains just base comparison logic. \n\nIt works well with numbers...\n\n```ts\nconst array = [17, 4, -17, 42, -3, 0];\n\n// [-17, -3, 0, 4, 17, 42]\narray.slice().sort(asc); \n// [42, 17, 4, 0, -3, -17]\narray.slice().sort(desc); \n```\n\nAnd with strings...\n\n```ts\nconst array = ['aaa', 'bax', 'a', 'x', 'ax', 'ab', 'ba', 'bx'];\n\n// [\"a\", \"aaa\", \"ab\", \"ax\", \"ba\", \"bax\", \"bx\", \"x\"]\narray.slice().sort(asc); \n// [\"x\", \"bx\", \"bax\", \"ba\", \"ax\", \"ab\", \"aaa\", \"a\"]\narray.slice().sort(desc);\n```\n\nEven with dates...\n\n```ts\nconst array = [new Date(2018, 0, 1), new Date(2017, 0, 1), new Date(2019, 0, 1)];\n\n// [Date(2017, 0, 1), Date(2018, 0, 1), Date(2019, 0, 1)]\narray.slice().sort(asc); \n// [Date(2019, 0, 1), Date(2018, 0, 1), Date(2017, 0, 1)]\narray.slice().sort(desc);\n```\n\nActually it works well with everything comparable by `\u003e` and `\u003c`. \n\n**NOTE** Every values which are neither `\u003e` nor `\u003c` are equal.\n\nAs you can see below, the initial order remains. If you want to sort by value of some item property, take a look on `map` function.\n\n```ts\nvar array1 = [{a: 1}, {a: 5}];\nvar array2 = [{a: 5}, {a: 1}];\n\narray1.slice().sort(asc);  // [{a: 1}, {a: 5}]\narray2.slice().sort(asc);  // [{a: 5}, {a: 1}]\narray1.slice().sort(desc); // [{a: 1}, {a: 5}]\narray2.slice().sort(desc); // [{a: 5}, {a: 1}]\n```\n\n*****\n\n### Functional way\n\n#### Function `reverse(comparator)`\nJust swap comparator args.\n```ts\nimport { asc, cmp, reverse } from 'type-comparator';\n\nconst functionalCmp = reverse(asc);\nconst array = [17, 4, -17, 42, -3, 0];\n\n// [ 42, 17, 4, 0, -3, -17 ]\narray.slice().sort(functionalCmp);  \n```\n\n#### Function `map(mapper, comparator)`\nMaps each args with `mapper` and apply `comparator`.\n\n```ts\nimport { asc, cmp, map } from 'type-comparator';\n\nconst mapper = x =\u003e x.a;\nconst comparator = map(mapper, asc);\nconst array = [{ a: 15 }, { a: 5 }];\n\n// [ { a: 5 }, { a: 15 } ]\narray.slice().sort(comparator);\n```\n\n#### Function `condition(conditionFn, comparatorA, comparatorB)`\nHas following logic:\n - Applies `comparatorA`, if both args satisfy `conditionFn`.\n - Applies `comparatorB`, if both args do not satisfy `conditionFn`.\n - Returns positive value, if only first arg satisfies `conditionFn`.\n - Returns negative value, if only second arg satisfies `conditionFn`.\n\n```ts\nimport { asc, cmp, condition } from 'type-comparator';\n\nconst conditionFn = x =\u003e x % 2 === 0;\nconst comparator = condition(conditionFn, asc, desc);\nconst array = [17, 4, -17, 42, -3, 0];\n\n// [ 17, -3, -17, 0, 4, 42 ]\narray.slice().sort(comparator);\n```\n\n#### Function `queue(comparators)`\nApplies first comparator from `comparators`. \n - If comparator returns non-zero value, returns it as result.\n - If comparator returns `0`, apply next comparator from `comparators`. \n - If there is no more comparator in `comparators` list, returns `0`.\n\n```ts\nimport { asc, cmp, desc, map, queue } from 'type-comparator';\n\nconst comparator = queue([\n    map(x =\u003e x.name, asc),\n    map(x =\u003e x.age, desc),\n]);\nconst array = [\n    { name: 'Alex', age: 21 },\n    { name: 'Jane', age: 19 },\n    { name: 'Alex', age: 26 },\n];\n\n// [ \n//    { name: 'Alex', age: 26 },\n//    { name: 'Alex', age: 21 },\n//    { name: 'Jane', age: 19 } \n// ]\narray.slice().sort(comparator);\n```\n\n******\n\n### Chaining way\n\n#### Basic usage\n`cmp()` - just starts chaining.\n`.use(comparator)` - applies comparator and terminates chaining.\n\n**Note:** `use()` chain can work with any comparator function (not only produced by `type-comparator`)\n\n```ts\nimport { asc, cmp } from 'type-comparator';\n\n// same as just `asc` function\nconst comparator1 = cmp().use(asc);\n\n// works like `asc` but just for numbers\nconst comparator2 = cmp().use((a, b) =\u003e a - b); \n\n// not a lot of sense, but it's possible\nconst comparator3 = cmp().use(comparator1); \n```\n\n#### Chain `.reverse()`\n\n```ts\nimport { asc, cmp, reverse } from 'type-comparator';\n\nconst comparator = cmp().reverse().use(asc);\nconst array = [17, 4, -17, 42, -3, 0];\n\n// [ 42, 17, 4, 0, -3, -17 ]\narray.slice().sort(comparator);\n```\n#### Chain `.map(mapper)`\n\n```ts\nimport { asc, cmp, map } from 'type-comparator';\n\nconst mapper = x =\u003e x.a;\nconst comparator = cmp().map(mapper).use(asc);\nconst array = [{ a: 15 }, { a: 5 }];\n\n// [ { a: 5 }, { a: 15 } ]\narray.slice().sort(comparator);\n```\n\n#### Chain `.if(conditionFn)`\nChecks `conditionFn` for each arg. \n - Applies next `.then` chain, if both args satisfy `conditionFn`.\n - Applies next `.else`/`.elif` chain, if both args do not satisfy `conditionFn`.\n - Returns positive value, if only first arg satisfies `conditionFn`.\n - Returns negative value, if only second arg satisfies `conditionFn`.\n\n**Note:** After `.if()` chain, only `.then` chain is available.\n```ts\nimport { asc, cmp } from 'type-comparator';\n\nconst conditionFn = x =\u003e x % 4 === 0;\nconst conditionFn2 = x =\u003e x % 2 === 0;\nconst chainingCmp = cmp()\n    .if(conditionFn).then(asc)\n    .elif(conditionFn2).then(asc)\n    .else(asc);\nconst array = [17, 4, -17, 42, -3, 0];\n\n// [ -17, -3, 17, 42, 0, 4 ]\narray.slice().sort(chainingCmp);\n```\n\n#### Chain `.then(comparator)`\nApplies `comparator`, if condition from previous `.if()`chain satisfies for both args.\n\n**Note:** After `.then()` chain, only `.elif()` or `.else()` chains are available.\n**Note:** `.then()` chain is available only after `.if()` or `.elif()` chains.\n\n#### Chain `.elif(conditionFn)`\nWorks same `.if()` chain.\n\n**Note:** After `.elif()` chain, only `.then()` chain is available.\n**Note:** `.elif()` chain is available only after `.then()` chain.\n\n#### Chain `.else(comparator)`\nApplies `comparator`, if both args do not satisfy comparators from previous `.if()`/`.elif` chains.\n\n**Note:** `.else()` chain is available only after `.then()` chain.\n**Note:** `.else()` chain finishes chaining and returns result comparator function.\n\n#### Chain `.use(comparators)` \nWorks same as `queue(comparators)` and terminates chaining.\n\n```ts\nimport { asc, cmp, desc } from 'type-comparator';\n\nconst comparator = cmp().use([\n    cmp().map(x =\u003e x.name).use(asc),\n    cmp().map(x =\u003e x.age).use(desc),\n]);\nconst array = [\n    { name: 'Alex', age: 21 },\n    { name: 'Jane', age: 19 },\n    { name: 'Alex', age: 26 },\n];\n\n// [ \n//    { name: 'Alex', age: 26 },\n//    { name: 'Alex', age: 21 },\n//    { name: 'Jane', age: 19 } \n// ]\narray.slice().sort(comparator);    \n```\n\n*********************************\n\n### Something more complex?... Ok!\n```ts\nimport { asc, cmp, desc } from 'type-comparator';\n\nconst comparator = cmp()\n    .map(x =\u003e x.a)\n    .use([\n        cmp()\n            .map(x =\u003e x.b)\n            .use(desc),\n        cmp()\n            .if(x =\u003e (x.b + x.c) % 2 === 0)\n            .map(x =\u003e x.c)\n            .use(asc),\n    ]);\n\nconst array = [\n    { a: { b: 1, c: 7 } },\n    { a: { b: 1, c: 6 } },\n    { a: { b: 1, c: 5 } },\n    { a: { b: 1, c: 4 } },\n    { a: { b: 1, c: 3 } },\n    { a: { b: 3, c: 2 } },\n];\n\n// [\n//     { a: { b: 3, c: 2 } },\n//     { a: { b: 1, c: 4 } },\n//     { a: { b: 1, c: 6 } },\n//     { a: { b: 1, c: 3 } },\n//     { a: { b: 1, c: 5 } },\n//     { a: { b: 1, c: 7 } },\n// ]\narray.slice().sort(comparator);\n```\n\n****\n### Q\u0026A\n\n**Q:** Should `reverse(cmp)` be equals to reversed array with `cmp` ?\n\n**A:** In general, it should not. \n`Array.prototype.reverse` just reverse all elements order, regardless its values.\nWhen comparator suppose both values are equal, it returns `0`. And these elements save original order.\n\n```ts\nconst array = [1, 2, 4];\nconst comparator = cmp().map(x =\u003e x % 2 === 0).use(asc);\n\n// [2, 4, 1]\narray.slice().sort(reverse(comparator));\n\n// [4, 2, 1]\narray.slice().sort(comparator).reverse();\n```\n\n\n## Support\n\nPlease [open an issue](https://github.com/lightness/type-comparator/issues/new) for support.\n\n## Contributing\n\nPlease contribute using [Github Flow](https://guides.github.com/introduction/flow/). Create a branch, add commits, and [open a pull request](https://github.com/lightness/type-comparator/compare/).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flightness%2Ftype-comparator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flightness%2Ftype-comparator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flightness%2Ftype-comparator/lists"}