@@ -90,91 +90,97 @@ function printTab(nb) {
9090 * algorithm should not matter to the caller of the methods, which is why it is not noted in the
9191 * documentation.
9292 */
93- let editDistanceCurrent = [ ] ;
94- let editDistancePrev = [ ] ;
95- let editDistancePrevPrev = [ ] ;
96- function editDistance ( a , b , limit ) {
97- // Ensure that `b` is the shorter string, minimizing memory use.
98- if ( a . length < b . length ) {
99- const aTmp = a ;
100- a = b ;
101- b = aTmp ;
102- }
103-
104- const minDist = a . length - b . length ;
105- // If we know the limit will be exceeded, we can return early.
106- if ( minDist > limit ) {
107- return limit + 1 ;
108- }
109-
110- // Strip common prefix.
111- // We know that `b` is the shorter string, so we don't need to check
112- // `a.length`.
113- while ( b . length > 0 && b [ 0 ] === a [ 0 ] ) {
114- a = a . substring ( 1 ) ;
115- b = b . substring ( 1 ) ;
116- }
117- // Strip common suffix.
118- while ( b . length > 0 && b [ b . length - 1 ] === a [ a . length - 1 ] ) {
119- a = a . substring ( 0 , a . length - 1 ) ;
120- b = b . substring ( 0 , b . length - 1 ) ;
121- }
122-
123- // If either string is empty, the distance is the length of the other.
124- // We know that `b` is the shorter string, so we don't need to check `a`.
125- if ( b . length === 0 ) {
126- return minDist ;
127- }
128-
129- const aLength = a . length ;
130- const bLength = b . length ;
131-
132- for ( let i = 0 ; i <= bLength ; ++ i ) {
133- editDistanceCurrent [ i ] = 0 ;
134- editDistancePrev [ i ] = i ;
135- editDistancePrevPrev [ i ] = Number . MAX_VALUE ;
136- }
137-
138- // row by row
139- for ( let i = 1 ; i <= aLength ; ++ i ) {
140- editDistanceCurrent [ 0 ] = i ;
141- const aIdx = i - 1 ;
142-
143- // column by column
144- for ( let j = 1 ; j <= bLength ; ++ j ) {
145- const bIdx = j - 1 ;
146-
147- // There is no cost to substitute a character with itself.
148- const substitutionCost = a [ aIdx ] === b [ bIdx ] ? 0 : 1 ;
149-
150- editDistanceCurrent [ j ] = Math . min (
151- // deletion
152- editDistancePrev [ j ] + 1 ,
153- // insertion
154- editDistanceCurrent [ j - 1 ] + 1 ,
155- // substitution
156- editDistancePrev [ j - 1 ] + substitutionCost
157- ) ;
158-
159- if ( ( i > 1 ) && ( j > 1 ) && ( a [ aIdx ] === b [ bIdx - 1 ] ) && ( a [ aIdx - 1 ] === b [ bIdx ] ) ) {
160- // transposition
161- editDistanceCurrent [ j ] = Math . min (
162- editDistanceCurrent [ j ] ,
163- editDistancePrevPrev [ j - 2 ] + 1
93+ const editDistanceState = {
94+ current : [ ] ,
95+ prev : [ ] ,
96+ prevPrev : [ ] ,
97+ calculate : function calculate ( a , b , limit ) {
98+ // Ensure that `b` is the shorter string, minimizing memory use.
99+ if ( a . length < b . length ) {
100+ const aTmp = a ;
101+ a = b ;
102+ b = aTmp ;
103+ }
104+
105+ const minDist = a . length - b . length ;
106+ // If we know the limit will be exceeded, we can return early.
107+ if ( minDist > limit ) {
108+ return limit + 1 ;
109+ }
110+
111+ // Strip common prefix.
112+ // We know that `b` is the shorter string, so we don't need to check
113+ // `a.length`.
114+ while ( b . length > 0 && b [ 0 ] === a [ 0 ] ) {
115+ a = a . substring ( 1 ) ;
116+ b = b . substring ( 1 ) ;
117+ }
118+ // Strip common suffix.
119+ while ( b . length > 0 && b [ b . length - 1 ] === a [ a . length - 1 ] ) {
120+ a = a . substring ( 0 , a . length - 1 ) ;
121+ b = b . substring ( 0 , b . length - 1 ) ;
122+ }
123+
124+ // If either string is empty, the distance is the length of the other.
125+ // We know that `b` is the shorter string, so we don't need to check `a`.
126+ if ( b . length === 0 ) {
127+ return minDist ;
128+ }
129+
130+ const aLength = a . length ;
131+ const bLength = b . length ;
132+
133+ for ( let i = 0 ; i <= bLength ; ++ i ) {
134+ this . current [ i ] = 0 ;
135+ this . prev [ i ] = i ;
136+ this . prevPrev [ i ] = Number . MAX_VALUE ;
137+ }
138+
139+ // row by row
140+ for ( let i = 1 ; i <= aLength ; ++ i ) {
141+ this . current [ 0 ] = i ;
142+ const aIdx = i - 1 ;
143+
144+ // column by column
145+ for ( let j = 1 ; j <= bLength ; ++ j ) {
146+ const bIdx = j - 1 ;
147+
148+ // There is no cost to substitute a character with itself.
149+ const substitutionCost = a [ aIdx ] === b [ bIdx ] ? 0 : 1 ;
150+
151+ this . current [ j ] = Math . min (
152+ // deletion
153+ this . prev [ j ] + 1 ,
154+ // insertion
155+ this . current [ j - 1 ] + 1 ,
156+ // substitution
157+ this . prev [ j - 1 ] + substitutionCost
164158 ) ;
159+
160+ if ( ( i > 1 ) && ( j > 1 ) && ( a [ aIdx ] === b [ bIdx - 1 ] ) && ( a [ aIdx - 1 ] === b [ bIdx ] ) ) {
161+ // transposition
162+ this . current [ j ] = Math . min (
163+ this . current [ j ] ,
164+ this . prevPrev [ j - 2 ] + 1
165+ ) ;
166+ }
165167 }
168+
169+ // Rotate the buffers, reusing the memory
170+ const prevPrevTmp = this . prevPrev ;
171+ this . prevPrev = this . prev ;
172+ this . prev = this . current ;
173+ this . current = prevPrevTmp ;
166174 }
167175
168- // Rotate the buffers, reusing the memory
169- const prevPrevTmp = editDistancePrevPrev ;
170- editDistancePrevPrev = editDistancePrev ;
171- editDistancePrev = editDistanceCurrent ;
172- editDistanceCurrent = prevPrevTmp ;
173- }
176+ // `prev` because we already rotated the buffers.
177+ const distance = this . prev [ bLength ] ;
178+ return distance <= limit ? distance : ( limit + 1 ) ;
179+ } ,
180+ } ;
174181
175- // `prev` because we already rotated the buffers.
176- const distance = editDistancePrev [ bLength ] ;
177- return distance <= limit ? distance : ( limit + 1 ) ;
182+ function editDistance ( a , b , limit ) {
183+ return editDistanceState . calculate ( a , b , limit ) ;
178184}
179185
180186function initSearch ( rawSearchIndex ) {
0 commit comments