Source

fuzzy.js

/**
 * Performs a fuzzy search on a list of strings or objects.
 * If a list of objects, provided the prop extraction function so the search can find the correct field(s)
 * This is heavily inspired by (most of) the algorithm used by [Matt York's](https://github.com/myork/fuzzy) fuzzy search function,
 * however several features were not carried over and his implementation of that alrgorithm has been significantly changed to achieve a 25% speed improvement.
 * Please see his original work - called [fuzzy](https://www.npmjs.com/package/fuzzy) MIT - if you need some of his additional options.
 *
 * @function
 * @name fuzzy
 * @param {function} propFn A function which will extract all the fields which you wish to fuzzy search on. Omit if the list is a list of strings
 * @param {string} needle The search value itself
 * @param {boolean} [caseSensitive=false] Whether or not to perform a case-sensitive search
 * @param {Array<string> | Array<object>} arr An array of string values or objects which have string values to be searched on
 * @returns {Array<string> | Array<object>} The filtered list of search results
 */
function fuzzy(propFn, needle, caseSensitive, arr) {
  if (arr == null || !Array.isArray(arr) || arr.length === 0) return []
  if (typeof needle !== "string" || !needle.trim().length) return arr

  const scores = []
  const len = arr.length
  const needleLen = needle.length
  let idx = len

  const noodle = caseSensitive ? needle : needle.toLowerCase()
  const extractFn = typeof propFn !== "function" && caseSensitive
    ? v => v
    : typeof propFn !== "function"
      ? v => v.toLowerCase()
      : caseSensitive
        ? propFn
        : v => propFn(v).toLowerCase()

  while (--idx) {
    let score = 0
    const val = extractFn(arr[idx])
    const valLen = val.length
    if (noodle === val) {
      scores.push([Infinity, idx])
    } else if (valLen !== 0) {
      let valIdx = 0
      let matchedInNeedleIdx = 0
      let numOfCharsMatchedAtOnce = 0
      while (valIdx < valLen) {
        if (noodle[matchedInNeedleIdx] === val[valIdx]) {
          matchedInNeedleIdx++
          numOfCharsMatchedAtOnce++
        } else {
          numOfCharsMatchedAtOnce = 0
        }
        score += numOfCharsMatchedAtOnce
        valIdx++
      }
      if (matchedInNeedleIdx === needleLen) {
        scores.push([score, idx])
      }
    }
  }

  return scores.sort((a, b) => b[0] - a[0] || a[1] - b[1]).map(([_, index]) => arr[index])
}

export default fuzzy