{"id":13592023,"url":"https://github.com/EthanRutherford/fast-fuzzy","last_synced_at":"2025-04-08T18:31:37.478Z","repository":{"id":38428211,"uuid":"87028147","full_name":"EthanRutherford/fast-fuzzy","owner":"EthanRutherford","description":"Fast fuzzy search utility","archived":false,"fork":false,"pushed_at":"2023-01-06T01:37:24.000Z","size":787,"stargazers_count":373,"open_issues_count":5,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-02T07:51:32.782Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EthanRutherford.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":"2017-04-03T01:22:40.000Z","updated_at":"2024-10-01T14:33:43.000Z","dependencies_parsed_at":"2023-02-05T01:46:06.104Z","dependency_job_id":null,"html_url":"https://github.com/EthanRutherford/fast-fuzzy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EthanRutherford%2Ffast-fuzzy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EthanRutherford%2Ffast-fuzzy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EthanRutherford%2Ffast-fuzzy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EthanRutherford%2Ffast-fuzzy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EthanRutherford","download_url":"https://codeload.github.com/EthanRutherford/fast-fuzzy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223339358,"owners_count":17129313,"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":[],"created_at":"2024-08-01T16:01:04.936Z","updated_at":"2024-11-06T12:32:37.030Z","avatar_url":"https://github.com/EthanRutherford.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# fast-fuzzy [![Build Status](https://travis-ci.com/EthanRutherford/fast-fuzzy.svg?branch=master)](https://travis-ci.com/EthanRutherford/fast-fuzzy) [![npm](https://img.shields.io/npm/v/fast-fuzzy.svg)](https://www.npmjs.com/package/fast-fuzzy)\nFast fuzzy-search utility\n\n## methodology\nfast-fuzzy is a tiny, lightning-quick fuzzy-searching utility.\nThe ranking algorithm is a modification of [levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance)\nproposed by Peter H. Sellers ([paper](https://pdfs.semanticscholar.org/0517/aa6d420f66f74bd4b281e2ed0e2021f3d359.pdf)).\nfast-fuzzy also uses the [damerau-levenshtein distance](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance)\nby default, which, compared to normal levenshtein, punishes transpositions less.\n\nInputs are normalized before search.\nNormalization consists of standard utf8-normalization,\noptionally taking the lowercase of a string,\noptionally removing non-word characters,\nand optionally flattening/trimming whitespace.\nGraphemes, such as conjoined emoji 👨‍👩‍👧‍, are treated as single characters.\n\nInputs are scored from `0` to `1`, where a higher score indicates a closer match.\nWhen searching, results are returned in descending order of score.\nTies in score are broken by earliness of match (when using sellers substring match only).\nFurther ties are broken by favoring the candidate whose length is closest to the length of the search term.\nThis causes matches which are closer to exact full string matches to be effectively ranked higher.\nTies in length difference are broken by insertion order.\n\nLists of candidates are stored in a [trie](https://en.wikipedia.org/wiki/Trie) internally, which\navoids doing redundant work on candidates with common prefixes.\nAdditionally, when a subtree of the trie can be determined to have no string which could possibly\nscore \u003e= the threshold, the entire subtree is skipped.\nThis significantly improves search times compared to a bruteforce search.\n\nWhile the default options are to use Damerau and Sellers (transposition-friendly substring search),\neither of these options can be opted out of if the need arises.\n\n## exports\n| name | description | signature |\n| ---- | --------- | ------------ |\n| `fuzzy` | fuzzy ranking algorithm; returns match strength | `(term, candidate, options?) =\u003e score` |\n| `search` | for one-off searches; returns a sorted array of matches | `(term, candidates, options?) =\u003e matches` |\n| `Searcher` | for searching the same set of candidates multiple times; caches the constructed trie\u003csup\u003e1\u003c/sup\u003e | `N/A` |\n\n\u003csup\u003e1\u003c/sup\u003e it is recommended that you use a `Searcher` when searching the same set multiple times.\n`search` will create a new trie every time, and while this is relatively cheap, it can have an\nimpact on responsiveness if you intend to update search results in real time, i.e. while typing.\n\n### `Searcher` methods\n| name | description | signature |\n| ---- | --------- | ------------ |\n| `constructor` | supply the options and initial list of candidates | `(candidates?, options?) =\u003e searcher` |\n| `add` | add new candidates to the list | `(...candidates) =\u003e void` |\n| `search` | perform a search against the instance's candidates  |`(term, options?) =\u003e matches`\u003csup\u003e2\u003c/sup\u003e |\n\n\u003csup\u003e2\u003c/sup\u003e allows overriding the `threshold`, `returnMatchData`, and `useDamerau` options\n\n## options\n`Searcher` and `search` both take an options object for configuring behavior.\n\n| option | type | description | default |\n| ------ | ---- | ----------- | ------- |\n| keySelector | `Function` | selects the string(s)\u003csup\u003e3\u003c/sup\u003e to search when candidates are objects | `s =\u003e s`\n| threshold | `Number` | the minimum score that can be returned | `.6`\n| ignoreCase | `Bool` | normalize case by calling `toLower` on input and pattern | `true`\n| ignoreSymbols | `Bool` | strip non-word symbols\u003csup\u003e4\u003c/sup\u003e from input | `true`\n| normalizeWhitespace | `Bool`| normalize and trim whitespace | `true`\n| returnMatchData | `Bool` | return match data\u003csup\u003e5\u003c/sup\u003e | `false`\n| useDamerau | `Bool` | use damerau-levenshtein distance | `true`\n| useSellers | `Bool` | use the Sellers method for substring matching | `true`\n| useSeparatedUnicode | `Bool` | use separated unicode | `false`\n| sortBy | `sortKind` | defines which order results are returned in\u003csup\u003e6\u003c/sup\u003e | `bestMatch`\n\n\u003csup\u003e3\u003c/sup\u003e if the keySelector returns an array, the candidate will take the score of the highest scoring key.\n\n\u003csup\u003e4\u003c/sup\u003e `` `~!@#$%^\u0026*()-=_+{}[]\\|\\;':\",./\u003c\u003e? ``\n\n\u003csup\u003e5\u003c/sup\u003e in the form `{item, original, key, score, match: {index, length}}`. \nMatch index and length are in terms of the original, non-normalized string.\nAlso note that `match` will be `undefined` if `useSellers` is `false`.\n\n\u003csup\u003e6\u003c/sup\u003e the supported sortKinds are `insertOrder` and `bestMatch`\n\n`fuzzy` accepts a subset of these options (excluding keySelector, threshold, and sortBy) with the same defaults.\n\n## examples\nYou can call `fuzzy` directly to get a match score for a single string\n\n```javascript\nconst {fuzzy} = require(\"fast-fuzzy\");\n\nfuzzy(\"hello\", \"hello world\"); //returns 1\nfuzzy(\"word\", \"hello world\"); //returns .75\n\n//pass in custom options\nfuzzy(\"hello world\", \"hello  world\"); //returns 1\nfuzzy(\"hello world\", \"hello  world\", {normalizeWhitespace: false}); //returns .90909090...\n```\n\nUse `search` to search a list of strings or objects\n\n```javascript\nconst {search} = require(\"fast-fuzzy\");\n\nsearch(\"abc\", [\"def\", \"bcd\", \"cde\", \"abc\"]); //returns [\"abc\", \"bcd\"]\n\n//pass in a keySelector to search for objects\nsearch(\n    \"abc\",\n    [{name: \"def\"}, {name: \"bcd\"}, {name: \"cde\"}, {name: \"abc\"}],\n    {keySelector: (obj) =\u003e obj.name},\n);\n//returns [{name: \"abc\"}, {name: \"bcd\"}]\n\n//pass returnMatchData to receive the matchData for each result\nsearch(\"abc\", [\"def\", \"bcd\", \"cde\", \"abc\"], {returnMatchData: true});\n/* returns [{\n    item: 'abc', original: 'abc', key: 'abc', score: 1,\n    match: {index: 0, length: 3},\n}, { \n    item: 'bcd', original: 'bcd', key: 'bcd', score: 0.6666666666666667,\n    match: {index: 0, length: 2},\n}] */\n```\n\nUse `Searcher` in much the same way as `search`\n\n```javascript\nconst {Searcher} = require(\"fast-fuzzy\");\n\nconst searcher = new Searcher([\"def\", \"bcd\", \"cde\", \"abc\"]);\nsearcher.search(\"abc\"); //returns [\"abc\", \"bcd\"]\n\n//options are passed in on construction\nconst anotherSearcher = new Searcher(\n    [{name: \"thing1\"}, {name: \"thing2\"}],\n    {keySelector: (obj) =\u003e obj.name},\n);\n\n//some options can be overridden per call\nsearcher.search(\"abc\", {returnMatchData: true});\n/* returns [{\n    item: 'abc', original: 'abc', key: 'abc', score: 1,\n    match: {index: 0, length: 3},\n}, { \n    item: 'bcd', original: 'bcd', key: 'bcd', score: 0.6666666666666667,\n    match: {index: 0, length: 2},\n}] */\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEthanRutherford%2Ffast-fuzzy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FEthanRutherford%2Ffast-fuzzy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEthanRutherford%2Ffast-fuzzy/lists"}