{"version":3,"file":"match-sorter.mjs","names":[],"sources":["../src/match-sorter.ts"],"sourcesContent":["/* -------------------------------------------------------------------\n\n                       🗲 Storm Software - Stryke\n\n This code was released as part of the Stryke project. Stryke\n is maintained by Storm Software under the Apache-2.0 license, and is\n free for commercial and private use. For more information, please visit\n our licensing page at https://stormsoftware.com/licenses/projects/stryke.\n\n Website:                  https://stormsoftware.com\n Repository:               https://github.com/storm-software/stryke\n Documentation:            https://docs.stormsoftware.com/projects/stryke\n Contact:                  https://stormsoftware.com/contact\n\n SPDX-License-Identifier:  Apache-2.0\n\n ------------------------------------------------------------------- */\n\nimport { removeAccents } from \"./remove-accents\";\n\n/**\n * Forked from match-sorter by Kent C. Dodds\n */\n\ninterface KeyAttributes {\n  threshold?: Ranking;\n  maxRanking: Ranking;\n  minRanking: Ranking;\n}\ninterface RankingInfo {\n  rankedValue: string;\n  rank: Ranking;\n  keyIndex: number;\n  keyThreshold: Ranking | undefined;\n}\n\ninterface ValueGetterKey<ItemType> {\n  (item: ItemType): string | Array<string>;\n}\ninterface IndexedItem<ItemType> {\n  item: ItemType;\n  index: number;\n}\ninterface RankedItem<ItemType> extends RankingInfo, IndexedItem<ItemType> {}\n\ninterface BaseSorter<ItemType> {\n  (a: RankedItem<ItemType>, b: RankedItem<ItemType>): number;\n}\n\ninterface Sorter<ItemType> {\n  (matchItems: Array<RankedItem<ItemType>>): Array<RankedItem<ItemType>>;\n}\n\ninterface KeyAttributesOptions<ItemType> {\n  key?: string | ValueGetterKey<ItemType>;\n  threshold?: Ranking;\n  maxRanking?: Ranking;\n  minRanking?: Ranking;\n}\n\ntype KeyOption<ItemType> =\n  | KeyAttributesOptions<ItemType>\n  | ValueGetterKey<ItemType>\n  | string;\n\ninterface MatchSorterOptions<ItemType = unknown> {\n  keys?: ReadonlyArray<KeyOption<ItemType>>;\n  threshold?: Ranking;\n  baseSort?: BaseSorter<ItemType>;\n  keepDiacritics?: boolean;\n  sorter?: Sorter<ItemType>;\n}\ntype IndexableByString = Record<string, unknown>;\n\nconst rankings = {\n  CASE_SENSITIVE_EQUAL: 7,\n  EQUAL: 6,\n  STARTS_WITH: 5,\n  WORD_STARTS_WITH: 4,\n  CONTAINS: 3,\n  ACRONYM: 2,\n  MATCHES: 1,\n  NO_MATCH: 0\n} as const;\n\ntype Ranking = (typeof rankings)[keyof typeof rankings];\n\nconst defaultBaseSortFn: BaseSorter<unknown> = (a, b) =>\n  String(a.rankedValue).localeCompare(String(b.rankedValue));\n\n/**\n * Takes an array of items and a value and returns a new array with the items that match the given value\n * @param items - the items to sort\n * @param value - the value to use for ranking\n * @param options - Some options to configure the sorter\n * @returns - the new sorted array\n */\nfunction matchSorter<ItemType = string>(\n  items: ReadonlyArray<ItemType>,\n  value: string,\n  options: MatchSorterOptions<ItemType> = {}\n): Array<ItemType> {\n  const {\n    keys,\n    threshold = rankings.MATCHES,\n    baseSort = defaultBaseSortFn,\n    sorter = matchedItems =>\n      matchedItems.sort((a, b) => sortRankedValues(a, b, baseSort))\n  } = options;\n  const matchedItems = items.reduce(\n    (ret: Array<RankedItem<ItemType>>, item: ItemType, index: number) =>\n      reduceItemsToRanked(ret, item, index),\n    []\n  );\n\n  return sorter(matchedItems).map(({ item }) => item);\n\n  function reduceItemsToRanked(\n    matches: Array<RankedItem<ItemType>>,\n    item: ItemType,\n    index: number\n  ): Array<RankedItem<ItemType>> {\n    const rankingInfo = getHighestRanking(item, keys, value, options);\n    const { rank, keyThreshold = threshold } = rankingInfo;\n    if (rank >= keyThreshold) {\n      matches.push({\n        ...rankingInfo,\n        item,\n        index\n      });\n    }\n    return matches;\n  }\n}\n\nmatchSorter.rankings = rankings;\n\n/**\n * Gets the highest ranking for value for the given item based on its values for the given keys\n * @param item - the item to rank\n * @param keys - the keys to get values from the item for the ranking\n * @param value - the value to rank against\n * @param options - options to control the ranking\n * @returns The highest ranking\n */\nfunction getHighestRanking<ItemType>(\n  item: ItemType,\n  keys: ReadonlyArray<KeyOption<ItemType>> | undefined,\n  value: string,\n  options: MatchSorterOptions<ItemType>\n): RankingInfo {\n  if (!keys) {\n    // if keys is not specified, then we assume the item given is ready to be matched\n    const stringItem = item as unknown as string;\n\n    return {\n      // ends up being duplicate of 'item' in matches but consistent\n      rankedValue: stringItem,\n      rank: getMatchRanking(stringItem, value, options),\n      keyIndex: -1,\n      keyThreshold: options.threshold\n    };\n  }\n  const valuesToRank = getAllValuesToRank(item, keys);\n\n  return valuesToRank.reduce(\n    (\n      { rank, rankedValue, keyIndex, keyThreshold },\n      { itemValue, attributes },\n      i\n    ) => {\n      let newRank = getMatchRanking(itemValue, value, options);\n      let newRankedValue = rankedValue;\n      const { minRanking, maxRanking, threshold } = attributes;\n      if (newRank < minRanking && newRank >= rankings.MATCHES) {\n        newRank = minRanking;\n      } else if (newRank > maxRanking) {\n        newRank = maxRanking;\n      }\n      if (newRank > rank) {\n        rank = newRank as 0;\n        keyIndex = i;\n        keyThreshold = threshold;\n        newRankedValue = itemValue;\n      }\n      return {\n        rankedValue: newRankedValue,\n        rank,\n        keyIndex,\n        keyThreshold\n      };\n    },\n    {\n      rankedValue: item as unknown as string,\n      rank: rankings.NO_MATCH,\n      keyIndex: -1,\n      keyThreshold: options.threshold\n    }\n  );\n}\n\n/**\n * Gives a rankings score based on how well the two strings match.\n * @param testString - the string to test against\n * @param stringToRank - the string to rank\n * @param options - options for the match (like keepDiacritics for comparison)\n * @returns the ranking for how well stringToRank matches testString\n */\nfunction getMatchRanking<ItemType>(\n  testString: string,\n  stringToRank: string,\n  options: MatchSorterOptions<ItemType>\n): Ranking {\n  testString = prepareValueForComparison(testString, options);\n  stringToRank = prepareValueForComparison(stringToRank, options);\n\n  // too long\n  if (stringToRank.length > testString.length) {\n    return rankings.NO_MATCH;\n  }\n\n  // case sensitive equals\n  if (testString === stringToRank) {\n    return rankings.CASE_SENSITIVE_EQUAL;\n  }\n\n  // Lower casing before further comparison\n  testString = testString.toLowerCase();\n  stringToRank = stringToRank.toLowerCase();\n\n  // case insensitive equals\n  if (testString === stringToRank) {\n    return rankings.EQUAL;\n  }\n\n  // starts with\n  if (testString.startsWith(stringToRank)) {\n    return rankings.STARTS_WITH;\n  }\n\n  // word starts with\n  if (testString.includes(` ${stringToRank}`)) {\n    return rankings.WORD_STARTS_WITH;\n  }\n\n  // contains\n  if (testString.includes(stringToRank)) {\n    return rankings.CONTAINS;\n  } else if (stringToRank.length === 1) {\n    // If the only character in the given stringToRank\n    //   isn't even contained in the testString, then\n    //   it's definitely not a match.\n    return rankings.NO_MATCH;\n  }\n\n  // acronym\n  if (getAcronym(testString).includes(stringToRank)) {\n    return rankings.ACRONYM;\n  }\n\n  // will return a number between rankings.MATCHES and\n  // rankings.MATCHES + 1 depending  on how close of a match it is.\n  return getClosenessRanking(testString, stringToRank);\n}\n\n/**\n * Generates an acronym for a string.\n *\n * @param value - the string for which to produce the acronym\n * @returns the acronym\n */\nfunction getAcronym(value: string): string {\n  let acronym = \"\";\n  const wordsInString = value.split(\" \");\n  for (const wordInString of wordsInString) {\n    const splitByHyphenWords = wordInString.split(\"-\");\n    for (const splitByHyphenWord of splitByHyphenWords) {\n      acronym += splitByHyphenWord.slice(0, 1);\n    }\n  }\n  return acronym;\n}\n\n/**\n * Returns a score based on how spread apart the\n * characters from the stringToRank are within the testString.\n * A number close to rankings.MATCHES represents a loose match. A number close\n * to rankings.MATCHES + 1 represents a tighter match.\n * @param testString - the string to test against\n * @param stringToRank - the string to rank\n * @returns the number between rankings.MATCHES and\n * rankings.MATCHES + 1 for how well stringToRank matches testString\n */\nfunction getClosenessRanking(\n  testString: string,\n  stringToRank: string\n): Ranking {\n  let matchingInOrderCharCount = 0;\n  let charNumber = 0;\n  function findMatchingCharacter(\n    matchChar: string,\n    string: string,\n    index: number\n  ) {\n    for (let j = index, J = string.length; j < J; j++) {\n      const stringChar = string[j];\n      if (stringChar === matchChar) {\n        matchingInOrderCharCount += 1;\n        return j + 1;\n      }\n    }\n    return -1;\n  }\n  function getRanking(spread: number) {\n    const spreadPercentage = 1 / spread;\n    const inOrderPercentage = matchingInOrderCharCount / stringToRank.length;\n    const ranking = rankings.MATCHES + inOrderPercentage * spreadPercentage;\n\n    return ranking as Ranking;\n  }\n  const firstIndex = findMatchingCharacter(stringToRank[0]!, testString, 0);\n  if (firstIndex < 0) {\n    return rankings.NO_MATCH;\n  }\n  charNumber = firstIndex;\n  for (let i = 1, I = stringToRank.length; i < I; i++) {\n    const matchChar = stringToRank[i];\n    charNumber = findMatchingCharacter(matchChar!, testString, charNumber);\n    const found = charNumber > -1;\n    if (!found) {\n      return rankings.NO_MATCH;\n    }\n  }\n\n  const spread = charNumber - firstIndex;\n\n  return getRanking(spread);\n}\n\n/**\n * Sorts items that have a rank, index, and keyIndex\n * @param a - the first item to sort\n * @param b - the second item to sort\n * @returns -1 if a should come first, 1 if b should come first, 0 if equal\n */\nfunction sortRankedValues<ItemType>(\n  a: RankedItem<ItemType>,\n  b: RankedItem<ItemType>,\n  baseSort: BaseSorter<ItemType>\n): number {\n  const aFirst = -1;\n  const bFirst = 1;\n  const { rank: aRank, keyIndex: aKeyIndex } = a;\n  const { rank: bRank, keyIndex: bKeyIndex } = b;\n  const same = aRank === bRank;\n  if (same) {\n    if (aKeyIndex === bKeyIndex) {\n      // use the base sort function as a tie-breaker\n      return baseSort(a, b);\n    }\n    return aKeyIndex < bKeyIndex ? aFirst : bFirst;\n  }\n  return aRank > bRank ? aFirst : bFirst;\n}\n\n/**\n * Prepares value for comparison by stringifying it, removing diacritics (if specified)\n * @param value - the value to clean\n * @param options - options for the match (like keepDiacritics for comparison)\n * @returns the prepared value\n */\nfunction prepareValueForComparison<ItemType>(\n  value: string,\n  options: MatchSorterOptions<ItemType>\n): string {\n  // value might not actually be a string at this point (we don't get to choose)\n  // so part of preparing the value for comparison is ensure that it is a string\n  value = `${value}`; // toString\n  if (!options.keepDiacritics) {\n    value = removeAccents(value);\n  }\n  return value;\n}\n\n/**\n * Gets value for key in item at arbitrarily nested keypath\n * @param item - the item\n * @param key - the potentially nested keypath or property callback\n * @returns - an array containing the value(s) at the nested keypath\n */\nfunction getItemValues<ItemType>(\n  item: ItemType,\n  key: KeyOption<ItemType>\n): Array<string> {\n  if (typeof key === \"object\") {\n    key = key.key as string;\n  }\n  let value: string | Array<string> | null | unknown;\n  if (typeof key === \"function\") {\n    value = key(item);\n  } else if (item === null) {\n    value = null;\n  } else if (Object.hasOwnProperty.call(item, key)) {\n    value = (item as IndexableByString)[key];\n  } else if (key.includes(\".\")) {\n    return getNestedValues<ItemType>(key, item);\n  } else {\n    value = null;\n  }\n\n  // because `value` can also be undefined\n  if (value === null) {\n    return [];\n  }\n  if (Array.isArray(value)) {\n    return value;\n  }\n  return [String(value)];\n}\n\n/**\n * Given path: \"foo.bar.baz\"\n * And item: \\{foo: \\{bar: \\{baz: 'buzz'\\}\\}\\}\n *   -\\> 'buzz'\n *\n * @param path - a dot-separated set of keys\n * @param item - the item to get the value from\n */\nfunction getNestedValues<ItemType>(\n  path: string,\n  item: ItemType\n): Array<string> {\n  const keys = path.split(\".\");\n\n  type ValueA = Array<ItemType | IndexableByString | string>;\n  let values: ValueA = [item];\n\n  for (let i = 0, I = keys.length; i < I; i++) {\n    const nestedKey = keys[i];\n    let nestedValues: ValueA = [];\n\n    for (let j = 0, J = values.length; j < J; j++) {\n      const nestedItem = values[j];\n\n      if (nestedItem === null) {\n        continue;\n      }\n\n      if (nestedKey && Object.hasOwnProperty.call(nestedItem, nestedKey)) {\n        const nestedValue = (nestedItem as IndexableByString)[nestedKey];\n        if (nestedValue !== null) {\n          nestedValues.push(nestedValue as IndexableByString | string);\n        }\n      } else if (nestedItem && nestedKey === \"*\") {\n        // ensure that values is an array\n        nestedValues = [...nestedValues, nestedItem];\n      }\n    }\n\n    values = nestedValues;\n  }\n\n  if (Array.isArray(values[0])) {\n    // keep allowing the implicit wildcard for an array of strings at the end of\n    // the path; don't use `.flat()` because that's not available in node.js v10\n    const result: Array<string> = [];\n\n    return result.concat(...(values as Array<string>));\n  }\n  // Based on our logic it should be an array of strings by now...\n  // assuming the user's path terminated in strings\n  return values as Array<string>;\n}\n\n/**\n * Gets all the values for the given keys in the given item and returns an array of those values\n * @param item - the item from which the values will be retrieved\n * @param keys - the keys to use to retrieve the values\n * @returns objects with \\{itemValue, attributes\\}\n */\nfunction getAllValuesToRank<ItemType>(\n  item: ItemType,\n  keys: ReadonlyArray<KeyOption<ItemType>>\n) {\n  const allValues: Array<{ itemValue: string; attributes: KeyAttributes }> = [];\n  for (let j = 0, J = keys.length; j < J; j++) {\n    const key = keys[j];\n    const attributes = getKeyAttributes(key!);\n    const itemValues = getItemValues(item, key!);\n    for (let i = 0, I = itemValues.length; i < I; i++) {\n      allValues.push({\n        itemValue: itemValues[i]!,\n        attributes\n      });\n    }\n  }\n  return allValues;\n}\n\nconst defaultKeyAttributes = {\n  maxRanking: Infinity as Ranking,\n  minRanking: -Infinity as Ranking\n};\n/**\n * Gets all the attributes for the given key\n * @param key - the key from which the attributes will be retrieved\n * @returns object containing the key's attributes\n */\nfunction getKeyAttributes<ItemType>(key: KeyOption<ItemType>): KeyAttributes {\n  if (typeof key === \"string\") {\n    return defaultKeyAttributes;\n  }\n  return {\n    ...defaultKeyAttributes,\n    ...key\n  };\n}\n\nexport { defaultBaseSortFn, matchSorter, rankings };\n\nexport type {\n  KeyAttributes,\n  KeyAttributesOptions,\n  KeyOption,\n  MatchSorterOptions,\n  RankingInfo,\n  ValueGetterKey\n};\n"],"mappings":"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"}