- What is it?
- Quick Start
- Installation
- Import
- What is new?
- Match function
- Filter function
- Benchmark results
- Tests
It is the fastest functions for fuzzy matching and items filtering.
https://codesandbox.io/s/fuzzytoolsexample-4r2ej
import { match, filter } from 'fuzzy-tools';
// Basic fuzzy match
match('fzz', 'fuzzy search');
// { score: 1 }
// Filter a list of strings
filter('fzz', ['fuzzy', 'funny', 'fazzy']);
// ['fuzzy', 'fazzy']npm install --save fuzzy-tools
ESM (recommended):
import { match, filter, matchString, matchList } from 'fuzzy-tools';CommonJS:
const { match, filter, matchString, matchList } = require('fuzzy-tools');Default import:
import fuzzyTools from 'fuzzy-tools';
fuzzyTools.match('fzz', 'fuzzy');v3.0.0
caseInsensitiveremoved (usecaseSensitive: trueinstead ofcaseInsensitive: false)masknot onlyString, butArray.of(String or RegExp). In case when it is array then search will be work as fuzzy but not by chars, but by words.['fuz', 'sea'](or['fuz', /sea/]) will not be found infuuuuzzzzzy seeeeearch, but will be found infuzzzzzy search.
match(mask, where, options):Object | null - returns result of string matching. Returns an object if mask matches where (e.g. 'fzz' in 'fuzzy'), null if it does not (e.g. 'fzza' in 'fuzzy').
| args | type | default | note |
|---|---|---|---|
| mask | String or Array.of(String or RegExp) | no | provide what you want to find ('fzz' or ['fuz', 'sea']) |
| where | String or Array.of(String) | no | destination string or array of strings |
| options | Object | {...} |
additional options (see below) |
-
mask-
String:
'fzz'will be found in'fuzzy search' -
Array.of(String or RegExp):
['fzz']will NOT be found in'fuzzy search'.[/fzz/]will NOT be found in'fuzzy search'.[/f/, /./, /zz/]will be found in'fuzzy search'.['fuz', 'sea']will be found in'fuzzy search'.['fuz', 'seaa']will NOT be found in'fuzzy search', because ALL words should be inwhere.match('fzz', 'fuzzy search'); // { score: 1 } match(['fzz'], 'fuzzy search'); // null match(['f', 'u', 'zz'], 'fuzzy search'); // { score: 1 } match(['f', /./, 'zz'], 'fuzzy search'); // { score: 1 } match(['f', /[a-z]/, 'zz'], 'fuzzy search'); // { score: 1 } match([/[a-x]{2}/, 'zz'], 'fuzzy search'); // { score: 1 } match(['fuz', 'rch'], 'fuzzy search'); // { score: 1 } match(['fuz', 'rch1111'], 'fuzzy search'); // null
-
-
where- String: result contains
scoreis score of matching.match('fuz', 'fuzzy'); // { score: 1 }
- Array of strings or Object({ key: String, key2: String }): result contains
scoreis min of scores of each string (score = min(match(mask, item[0]), match(mask, item[1]), ...)).match('fuz', ['fuzzy', 'it is fuzzy']); // { score: 1, matches: { 0: { score: 1 }, 1: { score: 1 } } } match('fuz', { v1: 'fuzzy', v2: 'it is fuzzy' }); // { score: 1, matches: { v1: { score: 1 }, v2: { score: 1 } } }
- Array of Object({ value: String, rate: Number(>0 and <=1) }): result contains
scoreis min of score of each value * rate (score = min(match(mask, item[0].value) / item[0].rate, ...))match('fuz', [ { value: 'fuzzy', rate: 0.75 }, { value: 'it is fuzzy', rate: 0.25 } ] ); match('fuz', { v1: { value: 'fuzzy', rate: 0.75 }, v2: { value: 'it is fuzzy', rate: 0.25 } } )
- String: result contains
-
optionsoption type default description caseSensitiveBoolean falseWhen true, character case matters (e.g.'FZZ'will NOT match'fuzzy'). Whenfalse, case is ignored.withScoreBoolean falseWhen true,scoreis computed for matched strings. Whenfalse,scoreis always1.withWrapperString or Function nullWhen provided, result contains wrapped— the original string with matched parts highlighted.String: template with {?}placeholder (e.g.'<b>{?}</b>'→fzzinfuzzy→'<b>f</b>u<b>zz</b>y')Function(word: String): String: (e.g. (word) => '<b>'+word+'</b>')withRangesBoolean falseWhen true, result containsranges— array of{ begin: Number, end: Number }with matched character ranges.ratesArray or Object {}Weights for each item in where. Values must be> 0and<= 1. See examples below.match('fuz', ['fuzzy', 'it is fuzzy'], { rates: [0.75, 0.25] } ) // or match('fuz', ['fuzzy', 'it is fuzzy'], { rates: { 0: 0.75, 1: 0.25 } } ) // equivalent to: match('fuz', { v1: { value: 'fuzzy', rate: 0.75 }, v2: { value: 'it is fuzzy', rate: 0.25 } } )
-
score- from0.001toinfinity, lower is better. IfwithScoreisfalsethenscoreis always1. -
matches- present whenwhereis Array or Object. An object where each key is the index/key and each value is:{ score: Number, original: String, index: Number | String, [wrapped: String], [ranges: Array] } -
wrapped- present ifwithWrapperis set. Contains the original string with matched parts wrapped. -
ranges- present ifwithRangesistrue. Array of{ begin: Number, end: Number }.
import { match } from 'fuzzy-tools';
match('fzz', 'fuzzy', { withScore: true }); // { score: 1.74 }
match('fzz', ['fu', 'fuzza'], { withScore: true }) // { score: 1.74, matches: {1:{score: 1.74}} }
match('fzz', [{ value: 'fuzza', rate: 0.75 }, { value: 'fuzzy', rate: 0.10 }], { withScore: true })
// { score: 2.32, matches: {0: {score: 2.32}, 1: {score: 17.4}} }
// score = Min(1.74 / 0.75, 1.74 / 0.10)
match('fzz', 'fuzzy'); // { score: 1 }
match('fZZ', 'fuzzy', { caseSensitive: true }); // null
match('fZZ', 'fuzzy', { withWrapper: '<i>{?}</i>' });
// { score: 1, wrapped: '<i>f</i>u<i>zz</i>y' }
match('fZZ', ['fuzzy'], { withWrapper: '<i>{?}</i>' });
// { score: 1, matches: {0: {score: 1, original: 'fuzzy', index: 0, wrapped: '<i>f</i>u<i>zz</i>y'}}}
match('fZZ', { v: 'fuzzy' }, { withWrapper: '<i>{?}</i>' });
// { score: 1, matches: { v: {score: 1, original: 'fuzzy', index: 'v', wrapped: '<i>f</i>u<i>zz</i>y'}}}
match('fZZ', 'fuzzy', { withWrapper: w => `<b>${w}</b>` });
// { score: 1, wrapped: '<b>f</b>u<b>zz</b>y' }
match('fZZ', ['fuzzy'], { withWrapper: w => `<b>${w}</b>`, withScore: true });
// { score: 1, matches: {0: {score: 1, wrapped: '<b>f</b>u<b>zz</b>y', original: 'fuzzy'}}}
match('fZZ', 'fuzzy', { withRanges: true });
// { score: 1, ranges: [{begin: 0, end: 0}, {begin: 2, end: 3}]}
match('fZZ', ['fuzzy'], { withRanges: true });
// { score: 1, matches: {0: {score: 1, ranges: [{begin: 0, end: 0}, {begin: 2, end: 3}], original: 'fuzzy'}}}import { match } from 'fuzzy-tools';
// or
// import { matchString } from 'fuzzy-tools';
match('fzz', 'fuzzy', { withScore: true }); // { score: 1.74 }
match('fzz', 'fuzzy'); // { score: 1 }
match('fZZ', 'fuzzy', { caseSensitive: true }); // null
match('fZZ', 'fuzzy', { withWrapper: '<i>{?}</i>', withScore: true });
// { score: 1.74, wrapped: '<i>f</i>u<i>zz</i>y' }
match('fZZ', 'fuzzy', { withWrapper: w => `<b>${w}</b>` });
// { score: 1, wrapped: '<b>f</b>u<b>zz</b>y' }
match('fZZ', 'fuzzy', { withRanges: true });
// { score: 1, ranges: [{begin: 0, end: 0}, {begin: 2, end: 3}]}import { match } from 'fuzzy-tools';
// or
// import { matchList } from 'fuzzy-tools';
match('fzz', ['fu', 'fuzza'], { withScore: true })
// { score: 1.74, matches: {1: {score: 1.74, original: 'fuzza', index: 1}} }
match('fzz', { v1: 'fu', v2: 'fuzza' })
// { score: 1, matches: {v2: {score: 1, original: 'fuzza', index: 'v2'}} }
match('fzz', [{ value: 'fuzza', rate: 0.75 }, { value: 'fuzzy', rate: 0.10 }], { withScore: true })
// {
// score: 2.3708148148148145,
// matches: {
// 0: { score: 2.3708148148148145, original: 'fuzza', rate: 0.75, index: 0 },
// 1: { score: 17.78111111111111, original: 'fuzzy', rate: 0.10, index: 1 }
// }
// }
// score = min(2.3708148148148145, 17.78111111111111)
// the same but more compact
match('fzz', ['fuzza', 'fuzzy'], { rates: [0.75, 0.10], withScore: true })
// {
// score: 2.3708148148148145,
// matches: {
// 0: { score: 2.3708148148148145, original: 'fuzza', rate: 0.75, index: 0 },
// 1: { score: 17.78111111111111, original: 'fuzzy', rate: 0.10, index: 1 }
// }
// }
// score = min(2.3708148148148145, 17.78111111111111)
match('fzz', {v1: { value: 'fuzza', rate: 0.75 }, v2: { value: 'fuzzy', rate: 0.10 } }, { withScore: true })
// {
// score: 2.3708148148148145,
// matches: {
// v1: { score: 2.3708148148148145, original: 'fuzza', rate: 0.75, index: 'v1' },
// v2: { score: 17.78111111111111, original: 'fuzzy', rate: 0.10, index: 'v2' }
// }
// }
// score = min(2.3708148148148145, 17.78111111111111)
// the same but more compact
match('fzz', { v1: 'fuzza', v2: 'fuzzy' }, { rates: { v1: 0.75, v2: 0.10 }, withScore: true })
// {
// score: 2.3708148148148145,
// matches: {
// v1: { score: 2.3708148148148145, original: 'fuzza', rate: 0.75, index: 'v1' },
// v2: { score: 17.78111111111111, original: 'fuzzy', rate: 0.10, index: 'v2' }
// }
// }
// score = min(2.3708148148148145, 17.78111111111111)
match('fZZ', ['fuzzy'], { caseSensitive: true });
// null
match('fZZ', ['fuzzy'], { withWrapper: '<i>{?}</i>' });
// { score: 1, matches: {0: {score: 1, index: 0, wrapped: '<i>f</i>u<i>zz</i>y', original: 'fuzzy'}}}
match('fZZ', ['fuzzy'], { withWrapper: w => `<b>${w}</b>`, withScore: true });
// { score: 1.74, matches: {0: {score: 1.74, index: 0, wrapped: '<b>f</b>u<b>zz</b>y', original: 'fuzzy'}}}
match('fZZ', ['fuzzy'], { withRanges: true });
// { score: 1.74, matches: {0: {score: 1.74, index: 0, ranges: [{begin: 0, end: 0}, {begin: 2, end: 3}], original: 'fuzzy'}}}filter(mask: String | Array<String>, items: Array, options: Object): Array - returns list of matched items.
| args | type | default | note |
|---|---|---|---|
| mask | String or Array<String> | no | what you want to find ('fzz') |
| items | Array | no | items list |
| options | Object | {...} |
additional options (see below) |
-
mask- read more in Match function -
items- Array of Strings
- Array of Objects
-
optionsoption type default description extractString, Array, Object or Function nullRequired to filter Array of Objects. Specifies which field(s) to match against. String: field name — extracts that field and matches against the mask Array: field names — each field is matched with rate 1Object: { fieldName: rateNumber }— field name is key, rate is valueFunction(item): String or Array — returns String, Array of Strings, Object of Strings, or Array of { value: String, rate: Number }itemWrapperFunction nullFunction(item, matchResult, context): any — transforms each matched item before pushing into result. If it returns a falsy value ( false,null,undefined,'',0), the item is skipped.context: { index: Number, result: Array }caseSensitiveBoolean falseWhen true, character case matters (e.g.'FZZ'will NOT match'fuzzy'). Whenfalse, case is ignored.withScoreBoolean falseWhen true,scoreis computed for matched strings. Whenfalse,scoreis always1.withWrapperString or Function nullWhen provided, match result for each item contains wrapped— the extracted value with matched parts highlighted.String: template with {?}placeholder (e.g.'<b>{?}</b>')Function(word: String): String: (e.g. (word) => '<b>'+word+'</b>')withRangesBoolean falseWhen true, match result for each item containsranges— array of{ begin: Number, end: Number }.
import { filter } from 'fuzzy-tools';
const data = ['fuzzy', 'fazzy', 'furry', 'funny', 'fuuuuuzzer'];
filter('fZZ', data);
// ['fuzzy', 'fazzy', 'fuuuuuzzer'];
filter('fZZ', data, { itemWrapper: item => item.toUpperCase() });
// ['FUZZY', 'FAZZY', 'FUUUUUZZER'];
filter('fZZ', data, { withWrapper: '<{?}>', itemWrapper: (item, match) => match.wrapped });
// ['<f>u<zz>y', '<f>a<zz>y', '<f>uuuuu<zz>er'];
filter('fZZ', data, { extract: (item) => item.slice(0, 4) });
// ['fuzzy', 'fazzy'];
filter('FZZ', data, { caseSensitive: true });
// [];
filter('FZZ', data, { caseSensitive: true, extract: item => item.toUpperCase() });
// ['fuzzy', 'fazzy', 'fuuuuuzzer'];import { filter } from 'fuzzy-tools';
const data = [{ v: 'fuzzy' }, { v: 'funny' }, { v: 'fuuuuuzzer'}];
filter('fZZ', data, { extract: 'v' });
// [{ v: 'fuzzy' }, { v: 'fuuuuuzzer'}];
filter('fZZ', data, { extract: 'vvv' });
// [];
filter('fZZ', data, { extract: 'v', itemWrapper: item => item.v });
// ['fuzzy', 'fuuuuuzzer'];
filter('fZZ', data, { extract: 'v', withWrapper: '<{?}>', itemWrapper: (item, m) => m.wrapped });
// ['<f>u<zz>y', '<f>uuuuu<zz>er'];
// if extract contains more than 1 field, then result of matching will contain matches array
filter('fZZ', data, {
extract: ['v', 'v2'],
withWrapper: '<{?}>',
itemWrapper: (item, m) => m.matches[0].wrapped
});
// ['<f>u<zz>y', '<f>uuuuu<zz>er'];- fuzzy-tools (this)
- fast-fuzzy (https://www.npmjs.com/package/fast-fuzzy)
- fuzzy (https://www.npmjs.com/package/fuzzy)
- fuzzyjs (https://www.npmjs.com/package/fuzzyjs)
- fuzzy.js (https://www.npmjs.com/package/fuzzy.js)
- fuse.js (https://www.npmjs.com/package/fuse.js)
- fuzzy-search (https://www.npmjs.com/package/fuzzy-search)
- generate pairs mask (random length) and string (255 or 5000 chars length). 5000 pairs.
- call match function from each library
- compare results
It is more real case - 5000 items with 255 length string
It is test case - 5000 items with 2000 length string. And it is not mistake, it is real results.
https://codesandbox.io/s/fuzzytoolsbenchmarkexample-t9fk1?file=/src/index.js


