1616 * Anurag Awasthi - updated to 0.2.0
1717 */
1818
19- const SEQUENTIAL_BONUS = 15 ; // bonus for adjacent matches
20- const SEPARATOR_BONUS = 30 ; // bonus if match occurs after a separator
21- const CAMEL_BONUS = 30 ; // bonus if match is uppercase and prev is lower
22- const FIRST_LETTER_BONUS = 15 ; // bonus if the first letter is matched
19+ export const DEFAULT_WEIGHTS = {
20+ sequentialBonus : 15 , // bonus for adjacent matches
21+ separatorBonus : 30 , // bonus if match occurs after a separator
22+ camelBonus : 30 , // bonus if match is uppercase and prev is lower
23+ firstLetterBonus : 15 , // bonus if the first letter is matched
2324
24- const LEADING_LETTER_PENALTY = - 5 ; // penalty applied for every letter in str before the first match
25- const MAX_LEADING_LETTER_PENALTY = - 15 ; // maximum penalty for leading letters
26- const UNMATCHED_LETTER_PENALTY = - 1 ;
25+ leadingLetterPenalty : - 5 , // penalty applied for every letter in str before the first match
26+ maxLeadingLetterPenalty : - 15 , // maximum penalty for leading letters
27+ unmatchedLetterPenalty : - 1
28+ } ;
2729
2830/**
2931 * Does a fuzzy search to find pattern inside a string.
30- * @param {* } pattern string pattern to search for
31- * @param {* } str string string which is being searched
32+ * @param {string } pattern pattern to search for
33+ * @param {string } str string which is being searched
34+ * @param {boolean } global whether to search for all matches or just one
3235 * @returns [boolean, number] a boolean which tells if pattern was
3336 * found or not and a search score
3437 */
35- export function fuzzyMatch ( pattern , str ) {
38+ export function fuzzyMatch ( pattern , str , global = false , weights = DEFAULT_WEIGHTS ) {
3639 const recursionCount = 0 ;
3740 const recursionLimit = 10 ;
3841 const matches = [ ] ;
3942 const maxMatches = 256 ;
4043
41- return fuzzyMatchRecursive (
42- pattern ,
43- str ,
44- 0 /* patternCurIndex */ ,
45- 0 /* strCurrIndex */ ,
46- null /* srcMatces */ ,
47- matches ,
48- maxMatches ,
49- 0 /* nextMatch */ ,
50- recursionCount ,
51- recursionLimit
52- ) ;
44+ if ( ! global ) {
45+ return fuzzyMatchRecursive (
46+ pattern ,
47+ str ,
48+ 0 /* patternCurIndex */ ,
49+ 0 /* strCurrIndex */ ,
50+ null /* srcMatches */ ,
51+ matches ,
52+ maxMatches ,
53+ 0 /* nextMatch */ ,
54+ recursionCount ,
55+ recursionLimit ,
56+ weights
57+ ) ;
58+ }
59+
60+ // Return all matches
61+ let foundMatch = true ,
62+ score ,
63+ idxs ,
64+ strCurrIndex = 0 ;
65+ const results = [ ] ;
66+
67+ while ( foundMatch ) {
68+ [ foundMatch , score , idxs ] = fuzzyMatchRecursive (
69+ pattern ,
70+ str ,
71+ 0 /* patternCurIndex */ ,
72+ strCurrIndex ,
73+ null /* srcMatches */ ,
74+ matches ,
75+ maxMatches ,
76+ 0 /* nextMatch */ ,
77+ recursionCount ,
78+ recursionLimit ,
79+ weights
80+ ) ;
81+ if ( foundMatch ) results . push ( [ foundMatch , score , [ ...idxs ] ] ) ;
82+ strCurrIndex = idxs [ idxs . length - 1 ] + 1 ;
83+ }
84+ return results ;
5385}
5486
5587/**
@@ -65,7 +97,8 @@ function fuzzyMatchRecursive(
6597 maxMatches ,
6698 nextMatch ,
6799 recursionCount ,
68- recursionLimit
100+ recursionLimit ,
101+ weights
69102) {
70103 let outScore = 0 ;
71104
@@ -110,7 +143,8 @@ function fuzzyMatchRecursive(
110143 maxMatches ,
111144 nextMatch ,
112145 recursionCount ,
113- recursionLimit
146+ recursionLimit ,
147+ weights
114148 ) ;
115149
116150 if ( matched ) {
@@ -134,16 +168,16 @@ function fuzzyMatchRecursive(
134168 outScore = 100 ;
135169
136170 // Apply leading letter penalty
137- let penalty = LEADING_LETTER_PENALTY * matches [ 0 ] ;
171+ let penalty = weights . leadingLetterPenalty * matches [ 0 ] ;
138172 penalty =
139- penalty < MAX_LEADING_LETTER_PENALTY ?
140- MAX_LEADING_LETTER_PENALTY :
173+ penalty < weights . maxLeadingLetterPenalty ?
174+ weights . maxLeadingLetterPenalty :
141175 penalty ;
142176 outScore += penalty ;
143177
144178 // Apply unmatched penalty
145179 const unmatched = str . length - nextMatch ;
146- outScore += UNMATCHED_LETTER_PENALTY * unmatched ;
180+ outScore += weights . unmatchedLetterPenalty * unmatched ;
147181
148182 // Apply ordering bonuses
149183 for ( let i = 0 ; i < nextMatch ; i ++ ) {
@@ -152,7 +186,7 @@ function fuzzyMatchRecursive(
152186 if ( i > 0 ) {
153187 const prevIdx = matches [ i - 1 ] ;
154188 if ( currIdx === prevIdx + 1 ) {
155- outScore += SEQUENTIAL_BONUS ;
189+ outScore += weights . sequentialBonus ;
156190 }
157191 }
158192
@@ -165,15 +199,15 @@ function fuzzyMatchRecursive(
165199 neighbor !== neighbor . toUpperCase ( ) &&
166200 curr !== curr . toLowerCase ( )
167201 ) {
168- outScore += CAMEL_BONUS ;
202+ outScore += weights . camelBonus ;
169203 }
170204 const isNeighbourSeparator = neighbor === "_" || neighbor === " " ;
171205 if ( isNeighbourSeparator ) {
172- outScore += SEPARATOR_BONUS ;
206+ outScore += weights . separatorBonus ;
173207 }
174208 } else {
175209 // First letter
176- outScore += FIRST_LETTER_BONUS ;
210+ outScore += weights . firstLetterBonus ;
177211 }
178212 }
179213
0 commit comments