{"id":15437635,"url":"https://github.com/blutorange/js-kagura","last_synced_at":"2026-04-20T10:32:05.109Z","repository":{"id":57288100,"uuid":"119650423","full_name":"blutorange/js-kagura","owner":"blutorange","description":"Utility methods for creating comparator functions.","archived":false,"fork":false,"pushed_at":"2018-04-16T11:26:10.000Z","size":451,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-23T01:58:46.698Z","etag":null,"topics":["comparator","javascript-library","node-js","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blutorange.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-31T07:23:16.000Z","updated_at":"2018-04-16T11:26:12.000Z","dependencies_parsed_at":"2022-09-20T01:12:41.429Z","dependency_job_id":null,"html_url":"https://github.com/blutorange/js-kagura","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/blutorange/js-kagura","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blutorange%2Fjs-kagura","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blutorange%2Fjs-kagura/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blutorange%2Fjs-kagura/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blutorange%2Fjs-kagura/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blutorange","download_url":"https://codeload.github.com/blutorange/js-kagura/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blutorange%2Fjs-kagura/sbom","scorecard":{"id":245154,"data":{"date":"2025-08-11","repo":{"name":"github.com/blutorange/js-kagura","commit":"6857240151e25739af7c79bd7a370cbb2d83001e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.5,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: The Unlicense: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v1.2.0 not signed: https://api.github.com/repos/blutorange/js-kagura/releases/10561322","Warn: release artifact v1.2.0 does not have provenance: https://api.github.com/repos/blutorange/js-kagura/releases/10561322"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"94 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-8w4h-3cm3-2pm2","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-hc9w-4p87-j549","Warn: Project is vulnerable to: GHSA-wg6g-ppvx-927h","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-9vvw-cc9w-f27h","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-hr2v-3952-633q","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2","Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-qrmc-fj45-qfc2","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-xf7w-r453-m56c","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-vfrc-7r7c-w9mx","Warn: Project is vulnerable to: GHSA-7wwv-vh3v-89cq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-xf5p-87ch-gxw2","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-6g33-f262-xjp4","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-2m39-62fm-q8r3","Warn: Project is vulnerable to: GHSA-mf6x-7mm4-x2g7","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-g7q5-pjjr-gqvp","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T07:27:36.377Z","repository_id":57288100,"created_at":"2025-08-17T07:27:36.377Z","updated_at":"2025-08-17T07:27:36.377Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32043001,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"online","status_checked_at":"2026-04-20T02:00:06.527Z","response_time":94,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-library","node-js","typescript"],"created_at":"2024-10-01T18:58:08.588Z","updated_at":"2026-04-20T10:32:05.091Z","avatar_url":"https://github.com/blutorange.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Utility methods for creating comparators, ie. `function (left,right)=\u003enumber`. Slightly more versatile than other packages I found.\n\n* ~ 700 bytes minified + gzipped without browser polyfills etc.\n\n# Documenation\n\n[Documentation for all methods with examples.](https://blutorange.github.io/js-kagura/)\n\n# Install\n\nThe drill:\n\n```sh\nnpm install --save kagura\n```\n\nTypings for typescript are available.\n\nUse the `dist.js` or `dist.min.js` for browser usage.\nExposes a global object `window.Kagura`.\n\n# Usage\n\nCompare objects by their `id` property:\n\n```javascript\n// import this lib\nconst { byKey } = require(\"kagura\")\n\n// create array and sort it with a custom comparator\nconst array = [ {id: 6}, {id: 3} ]\n\narray.sort(byKey(item =\u003e item.id));\n```\n\nIf you are comparing simply by some property, you can also use `byProp`:\n\n```javascript\nconst { byProp } = require(\"kagura\")\narray.sort(byProp(\"id\"))\n```\n\nCompare objects by their `data.id` property in descending order:\n\n```javascript\nbyProp(\"data.id\", inverse) // preferred\n\n// equivalently you couse use\ninvert(byProp(\"data.id\"))\nbyKey(item =\u003e - item.data.id)\nbyKey(item =\u003e item.data.id, inverse)\n```\n\nCompare objects by their `lastName` property first, then `firstName`, then `age`.\n\n```javascript\ncombine(\n  byProp(\"lastName\"),\n  byProp(\"firstName\"),\n  byProp(\"age\")\n)\n```\n\nFind all items equal to \"cake\".\n\n```javascript\n[\"cake\", \"chocolate\"].filter(equalTo(\"cake\"))\n\n[\"cake\", \"Cake\", \"chocolate\"].filter(equalTo(\"cake\"), ignoreCase)\n\n[{name: \"cake\"}, {name: \"chocolate\"}]\n  .filter(equalTo(\"cake\", byKey(\"name\")))\n\n[{name: \"cake\"}, {name: \"Cake\"}, {name: \"chocolate\"}]\n  .filter(equalTo(\"cake\", byKey(\"name\", ignoreCase)))\n```\n\nCompare objects by using the comparison operator \u003e and \u003c.\n\n```javascript\n[9,7,8].sort(natural) // =\u003e [7,8,9]\n[9,7,8].sort(inverse) // =\u003e [9,8,7]\n```\n\n# Handling undefined\n\n## Sort order of undefined\n\n`undefined` always compares as less than any other value. This is different than how\nArray#sort handles `undefined`, but in line with the idea that an undefined `string` represents a blank string, which sorts before other strings alphabetically. This\nbehaviour is also useful when working with comparisons on multiple properties.\n\nTo illustrate this, consider the following list of users, sorted first by their given\nname, then by their family name.\n\n```javascript\nconst user1 = {given: \"dave\", family: \"oxford\"};\nconst user2 = {given: \"dave\", family: \"carbide\"};\nconst user3 = {given: \"laura\", family: \"oxford\"};\nconst user4 = {given: \"laura\", family: \"borea\"};\nconst user5 = {given: \"odo\", family: \"frodo\"};\nconst users = [user1, user2, user3, user4, user5];\nconst comparator = combine(byProp(\"given\"), byProp(\"family\"));\nusers.sort(comparator);\n// =\u003e [user2, user1, user4, user3, user5]\n```\n\nNow assume we want to get all users whose given name is `laura`. We could iterate\nover all entries and apply a filter:\n\n```javascript\nusers.filter(user =\u003e user.given === \"laura\");\n``` \n\nThis works, but this takes `O(n)` time to run. Assuming the list is already\nsorted, a binary search runs in `O(log(n))` time. We construct a virtual user\nobject with only a given name `{given: \"laura\"}` and determine the position\nwhere it would sort in the ordered list.\n\n```javascript\nconst search = {given: \"laura\"};\nconst start = users.findIndex(user =\u003e comparator(search, user) \u003c= 0);\n// start === 2  \n```\n\nSince `undefined` sorts before any other value, the start position now points\nto the first user in the sorted list with a given name of `laura`. Now we can\nfind all `laura`s by iterating from the start position until we encounter a user\nwith a different given name.\n\n```javascript\nfor (let user = users[start]; user.given === `laura`; user= users[++start]) {\n  // do something with the user\n  console.log(user);\n}\n// =\u003e logs user4 and user3\n```\n\nThis approach also works well with binary search trees that always keep\ntheir elements sorted.\n\n## Sorting arrays\n\nThe built-in array sort method always sorts `undefined` last, irrespective of the given comparator. Use one of the wrapper methods\nto sort `undefined` properly:\n\n```javascript\nconst { sort } = require(\"kagura\");\n\n// Comparator \"natural\" sorts undefind before any other value.\n// But the built-in function always puts undefined last.\n[1,2,undefined,3].sort(natural);\n// =\u003e [1,2,3,undefined]\n\nsort([1,2,undefined,3], natural)\n// =\u003e [undefined, 1, 2, 3]\n```\n\n# Build\n\nProbably not going to work on Windows.\n\n```sh\ngit clone https://github.com/blutorange/js-kagura\ncd js-kagura\nnpm install\nnpm run build\n```\n\n# Change log\n\nI use the following keywords:\n\n- `Added` A new feature that is backwards-compatible.\n- `Changed` A change that is not backwards-compatible.\n- `Fixed` A bug or error that was fixed.\n\n## 1.2.0\n\n- Added sort wrappers, [see above](#sorting-arrays).\n- Fixed some typings regarding `undefined`. Some methods can handle undefined by setting the type parameter to `T|undefined` on the calling side.\n\n## 1.1.0\n\n- Updated all methods to handle `undefined` values. [See above](#handling-undefined)\n  for details.\n- Extracted common interfaces and types (Comparator, Predicate etc.) to\n  [their own package](https://npmjs.com/package/andross). This affects you only if\n  you are using typescript and are referring to these types explicitly; in this case\n  please import them from the package `andross` instead from this package `kagura`, see\n  below.\n\n```typescript\n// If using typescript, change this\nimport { Comparable, Comparator, Equator, KeyExtractor, Predicate } from \"kagura\";\n// to\nimport { Comparable, Comparator, Equator, KeyExtractor, Predicate } from \"andross\";\n```\n\n# Teh name\n\n[Senran Kagura](http://en.wikipedia.org/wiki/Senran_Kagura). Which part of the human body are they always concerned with and keep comparing?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblutorange%2Fjs-kagura","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblutorange%2Fjs-kagura","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblutorange%2Fjs-kagura/lists"}