{"id":10871,"library":"fast-fuzzy","title":"Fast Fuzzy Search Utility","description":"fast-fuzzy is a compact and high-performance JavaScript utility for fuzzy string matching, currently at version 1.12.0. It implements a modified Levenshtein distance algorithm, specifically Damerau-Levenshtein distance, which is more forgiving of transpositions. The library preprocesses inputs through UTF-8 normalization, optional lowercasing, symbol stripping, and whitespace normalization to ensure robust matching. It scores matches between 0 and 1, returning results sorted by score, then by match earliness, and finally by length proximity to the search term. For efficiency, especially when searching the same set of candidates repeatedly, it internally uses a trie data structure to cache work and prune non-matching subtrees, significantly outperforming brute-force approaches. While it offers a simple `search` function for one-off queries, the `Searcher` class is recommended for persistent collections due to its trie caching. The project appears to have a steady, though not strictly scheduled, release cadence, with ongoing maintenance.","status":"active","version":"1.12.0","language":"javascript","source_language":"en","source_url":"https://github.com/EthanRutherford/fast-fuzzy","tags":["javascript","fast","fuzzy","search","damerau","levenshtein","unicode","partial","match","typescript"],"install":[{"cmd":"npm install fast-fuzzy","lang":"bash","label":"npm"},{"cmd":"yarn add fast-fuzzy","lang":"bash","label":"yarn"},{"cmd":"pnpm add fast-fuzzy","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Use named import for `search` function; prefer `Searcher` for repeated searches.","wrong":"const search = require('fast-fuzzy').search","symbol":"search","correct":"import { search } from 'fast-fuzzy'"},{"note":"Instantiate `Searcher` for efficient searches against static candidate sets.","wrong":"const { Searcher } = require('fast-fuzzy')","symbol":"Searcher","correct":"import { Searcher } from 'fast-fuzzy'"},{"note":"`fuzzy` is a named export for the core ranking algorithm, not the default export.","wrong":"import fuzzy from 'fast-fuzzy'","symbol":"fuzzy","correct":"import { fuzzy } from 'fast-fuzzy'"}],"quickstart":{"code":"import { Searcher } from 'fast-fuzzy';\n\ninterface Product {\n  id: string;\n  name: string;\n  description: string;\n  tags: string[];\n}\n\nconst products: Product[] = [\n  { id: '1', name: 'Apple MacBook Pro', description: 'High-performance laptop', tags: ['laptop', 'apple'] },\n  { id: '2', name: 'Google Pixel 8 Pro', description: 'Advanced smartphone', tags: ['phone', 'android'] },\n  { id: '3', name: 'Microsoft Surface Laptop', description: 'Versatile notebook', tags: ['laptop', 'microsoft'] },\n  { id: '4', name: 'Apple Watch Series 9', description: 'Smartwatch with health features', tags: ['wearable', 'apple'] }\n];\n\n// Initialize Searcher with candidates and a keySelector for object properties\nconst productSearcher = new Searcher(products, {\n  keySelector: (obj: Product) => [obj.name, obj.description, ...obj.tags],\n  threshold: 0.7 // Only show results with a score of 0.7 or higher\n});\n\n// Perform a search\nconst results = productSearcher.search('apl watch');\n\nconsole.log('Search results for \"apl watch\":');\nresults.forEach(result => {\n  console.log(`- ${result.item.name} (Score: ${result.score.toFixed(2)})`);\n});\n\n// Add a new product dynamically\nproductSearcher.add({ id: '5', name: 'Samsung Galaxy Book', description: 'Lightweight laptop', tags: ['laptop', 'samsung'] });\n\nconst newResults = productSearcher.search('samsg book');\nconsole.log('\\nSearch results for \"samsg book\" after adding a new product:');\nnewResults.forEach(result => {\n  console.log(`- ${result.item.name} (Score: ${result.score.toFixed(2)})`);\n});","lang":"typescript","description":"Demonstrates initializing a `Searcher` with complex objects and a `keySelector`, performing a fuzzy search, and dynamically adding new candidates, with results filtered by a threshold."},"warnings":[{"fix":"For repeated searches against a consistent set of candidates, instantiate and reuse the `Searcher` class. Its internal trie is cached and updated incrementally, providing significantly better performance.","message":"Using the standalone `search` function repeatedly with the same candidate list can lead to performance degradation. Each call reconstructs the internal trie, which is inefficient for real-time or frequent searches.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Review and override the default options (`ignoreCase: true`, `ignoreSymbols: true`, `normalizeWhitespace: true`) in the `options` object passed to `search` or `Searcher` constructor if specific normalization behaviors are not desired.","message":"By default, `fast-fuzzy` normalizes inputs by ignoring case, ignoring symbols, and normalizing whitespace. While generally beneficial, these defaults might not be suitable for all use cases (e.g., case-sensitive searches).","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"When searching `Object[]` arrays, ensure `keySelector` is a function that returns the string(s) to be searched from each object. It can return a single string or an array of strings (e.g., `item => [item.name, item.description]`).","message":"The `keySelector` option is crucial when searching through arrays of objects. If not correctly configured, `fast-fuzzy` will default to treating the object itself as the string to search, which can lead to unexpected behavior or empty results.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Adjust the `threshold` option to a lower value (e.g., `0.4` or `0`) in the `options` object passed to `search` or `Searcher` to include more fuzzy matches. A lower threshold will also increase the number of results and potentially search time.","message":"The default `threshold` for matches is `0.6`. Results with a score below this value are not returned. If expected matches are missing, the `threshold` might be too high for the fuzziness required.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure that your `keySelector` function correctly returns a string or an array of strings for every candidate, and that the properties it accesses exist on all candidate objects. Add defensive checks if some properties might be missing.","cause":"The `keySelector` function is either missing or returning `undefined` or a non-string value for some candidates when `ignoreCase` is true, leading to an attempt to call string methods on an invalid type.","error":"TypeError: Cannot read properties of undefined (reading 'toLowerCase')"},{"fix":"Check the `threshold` option and consider lowering it. Verify that the `keySelector` correctly extracts search strings from your candidates. Also, review `ignoreCase`, `ignoreSymbols`, and `normalizeWhitespace` options to ensure they align with your search requirements.","cause":"This often happens due to an overly restrictive `threshold` option, or an incorrect `keySelector` that isn't targeting the intended search fields within objects, or overly aggressive normalization settings.","error":"Search results are empty or fewer than expected."},{"fix":"Refactor your code to use the `Searcher` class. Initialize a `Searcher` instance once with your candidate list, and then call its `search` method repeatedly. Use `add` or re-instantiate if the candidate list changes significantly.","cause":"You are likely using the `search` function directly for every keystroke without leveraging the `Searcher` class, which rebuilds the internal trie on each call.","error":"Performance is slow, especially when typing into a search box."}],"ecosystem":"npm"}