UNPKG

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