UNPKG

2.51 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = suggestionList;
7/**
8 * Copyright (c) 2015-present, Facebook, Inc.
9 *
10 * This source code is licensed under the MIT license found in the
11 * LICENSE file in the root directory of this source tree.
12 *
13 *
14 */
15
16/**
17 * Given an invalid input string and a list of valid options, returns a filtered
18 * list of valid options sorted based on their similarity with the input.
19 */
20function suggestionList(input, options) {
21 var optionsByDistance = Object.create(null);
22 var oLength = options.length;
23 var inputThreshold = input.length / 2;
24 for (var i = 0; i < oLength; i++) {
25 var distance = lexicalDistance(input, options[i]);
26 var threshold = Math.max(inputThreshold, options[i].length / 2, 1);
27 if (distance <= threshold) {
28 optionsByDistance[options[i]] = distance;
29 }
30 }
31 return Object.keys(optionsByDistance).sort(function (a, b) {
32 return optionsByDistance[a] - optionsByDistance[b];
33 });
34}
35
36/**
37 * Computes the lexical distance between strings A and B.
38 *
39 * The "distance" between two strings is given by counting the minimum number
40 * of edits needed to transform string A into string B. An edit can be an
41 * insertion, deletion, or substitution of a single character, or a swap of two
42 * adjacent characters.
43 *
44 * Includes a custom alteration from Damerau-Levenshtein to treat case changes
45 * as a single edit which helps identify mis-cased values with an edit distance
46 * of 1.
47 *
48 * This distance can be useful for detecting typos in input or sorting
49 *
50 * @param {string} a
51 * @param {string} b
52 * @return {int} distance in number of edits
53 */
54function lexicalDistance(aStr, bStr) {
55 if (aStr === bStr) {
56 return 0;
57 }
58
59 var i = void 0;
60 var j = void 0;
61 var d = [];
62 var a = aStr.toLowerCase();
63 var b = bStr.toLowerCase();
64 var aLength = a.length;
65 var bLength = b.length;
66
67 // Any case change counts as a single edit
68 if (a === b) {
69 return 1;
70 }
71
72 for (i = 0; i <= aLength; i++) {
73 d[i] = [i];
74 }
75
76 for (j = 1; j <= bLength; j++) {
77 d[0][j] = j;
78 }
79
80 for (i = 1; i <= aLength; i++) {
81 for (j = 1; j <= bLength; j++) {
82 var cost = a[i - 1] === b[j - 1] ? 0 : 1;
83
84 d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
85
86 if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
87 d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
88 }
89 }
90 }
91
92 return d[aLength][bLength];
93}
\No newline at end of file