{"id":1313,"date":"2018-09-26T08:34:49","date_gmt":"2018-09-26T07:34:49","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=1313"},"modified":"2022-11-21T10:16:04","modified_gmt":"2022-11-21T09:16:04","slug":"javascript-autosuggest-service-levenshtein-word-matching","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/javascript-autosuggest-service-levenshtein-word-matching\/","title":{"rendered":"Javascript autosuggest service (levenshtein + word matching)"},"content":{"rendered":"\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"javascript\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">function autoSuggestService() {\r\n\tvar exports = {};\r\n\tvar levenshtein = window.Levenshtein;\r\n\r\n\tfunction getDistance(str1, str2) {\r\n\t\t\/\/levenshtein.get('mikailovitch', 'Mikha\u00eflovitch', { useCollator: true});\r\n\t\treturn levenshtein.get(str1, str2, {\r\n\t\t\tuseCollator: true\r\n\t\t});\r\n\t}\r\n\r\n\tfunction escapeRegExp(string) {\r\n\t\treturn string.replace(\/[.*+?^${}()|[\\]\\\\]\/g, '\\\\&amp;'); \/\/ &amp; means the whole matched string \r\n\t}\r\n\tvar getWordsRegEx = new RegExp('\\\\b([^.,? ]+?)\\\\b', 'gm');\r\n\tvar getWordsRegEx = new RegExp('([^\\\\s.,;:]+)', 'gm');\r\n\tfunction getWords(str) { \r\n\t\tvar matches = []; \r\n\t\tvar match; \r\n\t\twhile (match = getWordsRegEx.exec(str)) { \r\n\t\t\tif (match[1].length > 0) { matches.push(match[1]); } \r\n\t\t} \r\n\t\treturn matches; \r\n\t}\r\n\tfunction replaceWord(str, find, replace) { \r\n\t\treturn str.replace(new RegExp('\\\\b' + escapeRegExp(find) + '\\\\b', 'gmi'), replace); \r\n\t}\r\n\tfunction multiFor(a, b, fn) { \r\n\t\tfor (var i = 0; i &lt; a.length; i++) { \r\n\t\t\tfor (var j = 0; j &lt; b.length; j++) { \r\n\t\t\t\tvar res = fn(a[i], b[j]); \r\n\t\t\t\tif (res === false) { break; } \r\n\t\t\t} \r\n\t\t} \r\n\t}\r\n\tfunction arrayCountMatch(a, b) { \r\n\t\tvar matchCount = 0; \r\n\t\tmultiFor(a, b, function (c, d) { if (c === d) { matchCount++; } }); \r\n\t\treturn matchCount; \r\n\t}\r\n\tfunction getUniqueWords(suggestions) {\r\n\t\tvar wordMap = {}; \r\n\t\tsuggestions.forEach(function (s) { var words = getWords((s.text || '')); \r\n\t\twords.forEach(function (w) { wordMap[w] = true; }); }); \r\n\t\treturn Object.keys(wordMap);\r\n\t}\r\n\tfunction findWordSuggestions(text, uniqueWords) {\r\n\t\tif (!text || text === '' || text === ' ') { return []; }\r\n\t\tvar words = getWords(text);\r\n\t\tvar results = [];\r\n\t\tmultiFor(words, uniqueWords, function (word, uniqueWord) {\r\n\t\t\tvar lowerWord = word.toLowerCase(); var lowerUniqueWord = uniqueWord.toLowerCase();\r\n\t\t\tif (lowerUniqueWord === lowerWord) {\r\n\t\t\t\treturn; \/\/ exact word no suggestions \r\n\t\t\t}\r\n\t\t\tif (lowerUniqueWord.charAt(0) !== lowerWord.charAt(0)) { return; }\r\n\t\t\tvar dist = getDistance(lowerWord, lowerUniqueWord.slice(0, lowerWord.length));\r\n\t\t\tvar score = (lowerWord.length \/ (dist + 1 * lowerWord.length));\r\n\t\t\tif (score >= 0.7) { results.push({ src: word, target: uniqueWord, score: score }); }\r\n\t\t});\r\n\t\tresults.sort(function (a, b) { return b.score - a.score; }); return results;\r\n\t}\r\n\tfunction findSuggestions(text, suggestions) {\r\n\t\tvar results = [];\r\n\t\tif (typeof text === 'string' &amp;&amp; text.length > 3) {\r\n\t\t\ttext = text.toLowerCase(); var words = getWords(text); if (words.length &lt; 1) { return results; }\r\n\t\t\tsuggestions.forEach(function (s) {\r\n\t\t\t\tvar str = (s.text || '').toLowerCase();\r\n\t\t\t\tif (str.indexOf(text) >= 0) { s.score = 1; results.push(s); return; }\r\n\t\t\t\tvar sugWords = getWords(str);\r\n\t\t\t\tvar matchCount = arrayCountMatch(words, sugWords);\r\n\t\t\t\tif (matchCount > 0) {\r\n\t\t\t\t\ts.score = matchCount \/ Math.min(words.length, sugWords.length);\r\n\t\t\t\t\tif (s.score > 0.2) { results.push(s); }\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t} results.sort(function (a, b) { return b.score - a.score; }); return results;\r\n\t}\r\n\texports = { findSuggestions: findSuggestions, findWordSuggestions: findWordSuggestions, getUniqueWords: getUniqueWords, replaceWord: replaceWord }; return exports;\r\n}\r\nfunction loadSuggestions() {\r\n\tvar suggestions = [{ text: '', value: '' }];\r\n\treturn suggestions;\r\n}\r\n\r\nvar _suggestions = null;\r\n\r\nfunction getSuggestions() {\r\n\tif (!_suggestions) {\r\n\t\t_suggestions = loadSuggestions();\r\n\t}\r\n\treturn _suggestions;\r\n}\r\n\r\nfunction getSuggestionsForText(text) {\r\n\treturn getSuggestions()\r\n\t\t.then(function (suggestions) {\r\n\t\t\treturn autoSuggestService.findSuggestions(text, suggestions);\r\n\t\t});\r\n}\r\n\r\nfunction getWordSuggestionsForText(text) {\r\n\treturn getSuggestions()\r\n\t\t.then(function (suggestions) {\r\n\t\t\tvar uniqueWords = autoSuggestService.getUniqueWords(suggestions);\r\n\t\t\treturn autoSuggestService.findWordSuggestions(text, uniqueWords);\r\n\t\t});\r\n}\r\n<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[5,4],"tags":[],"class_list":["post-1313","post","type-post","status-publish","format-standard","hentry","category-javascript","category-programming"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/1313","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=1313"}],"version-history":[{"count":3,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/1313\/revisions"}],"predecessor-version":[{"id":7020,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/1313\/revisions\/7020"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=1313"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=1313"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=1313"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}