let SCORE_MIN = -Infinity let SCORE_MAX = Infinity let SCORE_GAP_LEADING = -0.005 let SCORE_GAP_TRAILING = -0.005 let SCORE_GAP_INNER = -0.01 let SCORE_MATCH_CONSECUTIVE = 1.0 let SCORE_MATCH_SLASH = 0.9 let SCORE_MATCH_WORD = 0.8 let SCORE_MATCH_DOT = 0.6 def fzy arr, query, keyname="name" let needle = query.trim!.toLowerCase! return [] unless arr.length > 0 let scored = [] let M = new Array(100_000) let D = new Array(100_000) let B = new Array(100_000) for obj in arr continue unless obj.hasOwnProperty keyname let haystack = obj[keyname].trim!.toLowerCase! continue unless has_match needle, haystack obj.fzy_score = score(needle, haystack, M, D, B) sorted_insert obj, scored scored def score needle, haystack, M, D, match_bonus let n = needle.length let m = haystack.length if n < 1 or m < 1 return SCORE_MIN if n === m return SCORE_MAX if m > 1024 return SCORE_MIN compute needle, haystack, M, D, match_bonus M[(n - 1)*m + (m - 1)] def compute needle, haystack, M, D, match_bonus let n = needle.length let m = haystack.length precompute_bonus haystack, match_bonus for i in [0 .. n - 1] let prev_score = SCORE_MIN let gap_score = i === n - 1 ? SCORE_GAP_TRAILING : SCORE_GAP_INNER for j in [0 .. m - 1] let ij = i*m + j let pij = (i - 1)*m + (j - 1) if needle[i] === haystack[j] let score = SCORE_MIN if i === 0 score = (j * SCORE_GAP_LEADING) + match_bonus[j] elif j > 0 score = Math.max(M[pij] + match_bonus[j], D[pij] + SCORE_MATCH_CONSECUTIVE) D[ij] = score M[ij] = prev_score = Math.max(score, prev_score + gap_score) else D[ij] = SCORE_MIN M[ij] = prev_score = prev_score + gap_score def precompute_bonus haystack, match_bonus let m = haystack.length let last_ch = '/' for i in [0 .. m - 1] let ch = haystack[i] if last_ch === '/' match_bonus[i] = SCORE_MATCH_SLASH elif last_ch === '-' || last_ch === '_' || last_ch === ' ' match_bonus[i] = SCORE_MATCH_WORD elif last_ch === '.' match_bonus[i] = SCORE_MATCH_DOT else match_bonus[i] = 0 last_ch = ch def has_match needle, haystack let i = 0 let n = -1 let letter while letter = needle[i++] if (n = haystack.indexOf(letter, n + 1)) === -1 return no return yes def sorted_insert elem, arr let low = 0 let high = arr.length while low < high let mid = (low + high) >>> 1 if elem.fzy_score > arr[mid].fzy_score high = mid else low = mid + 1 arr.splice(low, 0, elem) export default fzy