Fast Fuzzy Search Utility
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.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'toLowerCase')
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.fixEnsure 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. -
Search results are empty or fewer than expected.
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.fixCheck 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. -
Performance is slow, especially when typing into a search box.
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.fixRefactor 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.
Warnings
- gotcha 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.
- gotcha 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).
- gotcha 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.
- gotcha 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.
Install
-
npm install fast-fuzzy -
yarn add fast-fuzzy -
pnpm add fast-fuzzy
Imports
- search
const search = require('fast-fuzzy').searchimport { search } from 'fast-fuzzy' - Searcher
const { Searcher } = require('fast-fuzzy')import { Searcher } from 'fast-fuzzy' - fuzzy
import fuzzy from 'fast-fuzzy'
import { fuzzy } from 'fast-fuzzy'
Quickstart
import { Searcher } from 'fast-fuzzy';
interface Product {
id: string;
name: string;
description: string;
tags: string[];
}
const products: Product[] = [
{ id: '1', name: 'Apple MacBook Pro', description: 'High-performance laptop', tags: ['laptop', 'apple'] },
{ id: '2', name: 'Google Pixel 8 Pro', description: 'Advanced smartphone', tags: ['phone', 'android'] },
{ id: '3', name: 'Microsoft Surface Laptop', description: 'Versatile notebook', tags: ['laptop', 'microsoft'] },
{ id: '4', name: 'Apple Watch Series 9', description: 'Smartwatch with health features', tags: ['wearable', 'apple'] }
];
// Initialize Searcher with candidates and a keySelector for object properties
const productSearcher = new Searcher(products, {
keySelector: (obj: Product) => [obj.name, obj.description, ...obj.tags],
threshold: 0.7 // Only show results with a score of 0.7 or higher
});
// Perform a search
const results = productSearcher.search('apl watch');
console.log('Search results for "apl watch":');
results.forEach(result => {
console.log(`- ${result.item.name} (Score: ${result.score.toFixed(2)})`);
});
// Add a new product dynamically
productSearcher.add({ id: '5', name: 'Samsung Galaxy Book', description: 'Lightweight laptop', tags: ['laptop', 'samsung'] });
const newResults = productSearcher.search('samsg book');
console.log('\nSearch results for "samsg book" after adding a new product:');
newResults.forEach(result => {
console.log(`- ${result.item.name} (Score: ${result.score.toFixed(2)})`);
});