{"id":18169884,"url":"https://github.com/karfau/scor","last_synced_at":"2026-04-30T14:37:28.259Z","repository":{"id":44997452,"uuid":"442170173","full_name":"karfau/scor","owner":"karfau","description":"Calculate scores for numeric values or items, and get the \"total score\" (aka \"arithmetic mean\" or \"weighted arithmetic mean\") from multiple scores.","archived":false,"fork":false,"pushed_at":"2022-01-14T18:08:10.000Z","size":111,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-27T18:28:08.760Z","etag":null,"topics":["deno","scores","typescript"],"latest_commit_sha":null,"homepage":"","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/karfau.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2021-12-27T13:36:38.000Z","updated_at":"2022-01-06T09:21:00.000Z","dependencies_parsed_at":"2022-09-13T12:50:53.855Z","dependency_job_id":null,"html_url":"https://github.com/karfau/scor","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/karfau/scor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karfau%2Fscor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karfau%2Fscor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karfau%2Fscor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karfau%2Fscor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karfau","download_url":"https://codeload.github.com/karfau/scor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karfau%2Fscor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32468009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"ssl_error","status_checked_at":"2026-04-30T13:12:06.837Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["deno","scores","typescript"],"created_at":"2024-11-02T15:00:21.770Z","updated_at":"2026-04-30T14:37:28.239Z","avatar_url":"https://github.com/karfau.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![latest version of scor package on deno.land](https://img.shields.io/github/v/tag/karfau/scor?label=deno.land/x/scor\u0026sort=semver)](https://deno.land/x/scor)\n\n# scor\n\nCalculate scores for numeric values or items, and get the \"total score\" (aka\n[arithmetic mean](https://en.wikipedia.org/wiki/Arithmetic_mean) or\n[weighted arithmetic mean](https://en.wikipedia.org/wiki/Weighted_arithmetic_mean))\nfrom multiple scores.\n\n## Usage\n\nImagine you\n\n- have a long list of items to work on, and you want to prioritize them\n- want to show the most relevant items to a user before showing more\n\nFor example, let's look at npm packages. Possible criteria are:\n\n- number of maintainers\n- number of dependencies (direct/transient)\n- time since last published version\n- version (major \u003c 1?) / dist-tags\n- weekly downloads\n- source code repo attributes (e.g. GitHub stars/forks)\n- quality?\n- ...?\n\nThe different relevant values come in very different \"shapes\". Once all the data\nis gathered per package, depending on the use case the different values are more\nor less relevant.\n\n```ts\nimport {\n  createToMean,\n  distributeWeights,\n  scorForItems,\n} from \"https://deno.land/x/scor/scor.ts\";\nimport { getPackagesData } from \"./npm.ts\";\n\nconst packages = await getPackagesData();\n\nconst scors = { // scorForItems uses `toValue` (1st parameter) to determine `min` and `max`\n  downloads: scorForItems(\n    // toValue converts an item to a numeric value, in this case with a log10 scale\n    (p) =\u003e Math.log10(p.downloads),\n    packages,\n  ),\n  maintainers: scorForItems((p) =\u003e p.maintainers.length, packages),\n};\n\n// one way to calculate indivudual scores for each item\nconst scores = packages.map((p) =\u003e ({\n  name: p.name,\n  downloadScore: scors.downloads.forItem(p),\n  maintainerScore: scors.maintainers.forItem(p),\n}));\n// the result could look like this (1 means highest score, 0 lowest score):\n// =\u003e [{downloads: 0.786, maintainers:0.2}, {downloads: 0.89, maintainers: 1}, {downloads:1, maintainers: 0}, ...]\n\n// or calculate the arithmetic mean per item\nconst scorePerItem = packages.map(createToMean(scors));\n// =\u003e [0.493, 0.945, 0.5]\n\n// or the weighted arithmetic mean\nconst weightedScorePerItem = packages.map(createToMean(\n  scors,\n  { downloads: 0.75, maintainers: 0.25 },\n));\n// =\u003e [0.31975, 0.45875, 0.375]\n\n// or as a list wihtout keys\nconst scorsList = [\n  scorForItems(\n    (p) =\u003e Math.log10(p.downloads),\n    packages,\n  ),\n  scorForItems((p) =\u003e p.maintainers.length, packages),\n];\n\nconst weightedScorePerItemL = packages.map(\n  createToMean(scorsList, [0.75, 0.25]),\n);\n// =\u003e [0.31975, 0.45875, 0.375] (of course the sam as above)\n\n// if you have many weights and some should be distributed:\ndistributeWeights(\n  [0.5, undefined, undefined],\n); // =\u003e [0.5, 0.25, 0.25]\n\ndistributeWeights(\n  { first: 0.7, second: undefined, third: undefined },\n); // =\u003e {first: 0.7, second: 0.15, third: 0.15}\n```\n\n## Concept and vision\n\nI experienced that such a \"rating system\", or \"weighted average score\", is not\nso easy to get completely right from scratch alongside collecting the data. It\nalso involves a lot of repetitive code that easily leaks into the rest of the\ncode.\n\n`scor` simplifies this by making certain assumptions:\n\n- All values are within a certain **range** (`min \u003c= value \u003c= max`).\n  - only numeric values are accepted, everything else throws\n- To use the (different) values as a **score** and easily compare all of them,\n  they need to be converted into the same `range`: between `0`(`value \u003c= min`)\n  and `1` (`value \u003e= max`)\n  - If the range is \"empty\" (`min === max`), the score is always 0\n  - values that are \"not numeric\" (see `isNumeric`) result in a score of 0 (to\n    avoid `NaN`)\n- The user fully controls the conversion of `item` to `value` (`toValue`):\n  - get deeply nested fields\n  - calculate from multiple fields\n  - convert data to a numeric value\n  - need the highest value to be the lowest score: `-1 * value`\n  - need some custom scale (e.g. logarithmic): `Math.log10(value)`\n  - ...\n- The user fully controls `min` and `max` values, but they can be derived from\n  `items` (also using `toValue`, see `getItemRange`).\n- All options for `scor` are optional and can be configured as a second step\n- Fail as early as possible (by throwing a specific `Error`):\n  - using a method that requires an optional value which has not been configured\n  - a required value is not numeric (beside cases mentioned above)\n- A `Scor` is immutable, \"set...\" methods create a new instance.\n- A `Scor` never keeps references to the items it is scoring.\n- Multiple `Scor`s can easily be combined into a single overall weighted score\n  per item, e.g. to use it for sorting\n\n## TODOs\n\nContributions are welcome!\n\n- [post about it and get feedback](https://dev.to/karfau/i-published-my-first-deno-package-4720)\n- Add support for weighted sum?\n- Add support for more kind of averages?\n  - https://en.wikipedia.org/wiki/Average#Summary_of_types\n  - https://en.wikipedia.org/wiki/Geometric_mean ?\n  - https://en.wikipedia.org/wiki/Harmonic_mean ?\n- publish to npm(?)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarfau%2Fscor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarfau%2Fscor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarfau%2Fscor/lists"}