suggestionList.js 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = suggestionList;
  6. /**
  7. * Given an invalid input string and a list of valid options, returns a filtered
  8. * list of valid options sorted based on their similarity with the input.
  9. */
  10. function suggestionList(input, options) {
  11. var optionsByDistance = Object.create(null);
  12. var inputThreshold = input.length / 2;
  13. for (var _i2 = 0; _i2 < options.length; _i2++) {
  14. var option = options[_i2];
  15. var distance = lexicalDistance(input, option);
  16. var threshold = Math.max(inputThreshold, option.length / 2, 1);
  17. if (distance <= threshold) {
  18. optionsByDistance[option] = distance;
  19. }
  20. }
  21. return Object.keys(optionsByDistance).sort(function (a, b) {
  22. return optionsByDistance[a] - optionsByDistance[b];
  23. });
  24. }
  25. /**
  26. * Computes the lexical distance between strings A and B.
  27. *
  28. * The "distance" between two strings is given by counting the minimum number
  29. * of edits needed to transform string A into string B. An edit can be an
  30. * insertion, deletion, or substitution of a single character, or a swap of two
  31. * adjacent characters.
  32. *
  33. * Includes a custom alteration from Damerau-Levenshtein to treat case changes
  34. * as a single edit which helps identify mis-cased values with an edit distance
  35. * of 1.
  36. *
  37. * This distance can be useful for detecting typos in input or sorting
  38. *
  39. * @param {string} a
  40. * @param {string} b
  41. * @return {int} distance in number of edits
  42. */
  43. function lexicalDistance(aStr, bStr) {
  44. if (aStr === bStr) {
  45. return 0;
  46. }
  47. var d = [];
  48. var a = aStr.toLowerCase();
  49. var b = bStr.toLowerCase();
  50. var aLength = a.length;
  51. var bLength = b.length; // Any case change counts as a single edit
  52. if (a === b) {
  53. return 1;
  54. }
  55. for (var i = 0; i <= aLength; i++) {
  56. d[i] = [i];
  57. }
  58. for (var j = 1; j <= bLength; j++) {
  59. d[0][j] = j;
  60. }
  61. for (var _i3 = 1; _i3 <= aLength; _i3++) {
  62. for (var _j = 1; _j <= bLength; _j++) {
  63. var cost = a[_i3 - 1] === b[_j - 1] ? 0 : 1;
  64. d[_i3][_j] = Math.min(d[_i3 - 1][_j] + 1, d[_i3][_j - 1] + 1, d[_i3 - 1][_j - 1] + cost);
  65. if (_i3 > 1 && _j > 1 && a[_i3 - 1] === b[_j - 2] && a[_i3 - 2] === b[_j - 1]) {
  66. d[_i3][_j] = Math.min(d[_i3][_j], d[_i3 - 2][_j - 2] + cost);
  67. }
  68. }
  69. }
  70. return d[aLength][bLength];
  71. }