modules searchWithError.js

/* Принцип: если было более 30% совпадений сочетаний 3 букв, то будет список отсортированный по количеству совпадений Если все сочетания 3х букв слова, которое ищем, нашлись в названии, которое 3 раза перевёрнуто по клавиатуре Привет Ghbdtn Privet Зкшмуе. Значит это то, что искали.. Иначе выставляем сколько % найденно. И потом сортируем по этим % */ export const searchWithError = { 'versionDate':'2023-08-10', 'minProbability' : 30, 'delay' : 300, 'quantityСharacters' : [2,3], 'data' : { 'list':[] }, /* Получение данных в виде ключ : текст для поиска так оно в более привычном и минимальном виде searchWithError.setDatabase({key:value, key:value}) */ 'setDatabase' : (data) => { searchWithError.data.list = Object.keys(data).map(key => ({ 'key': key, 'value': data[key], 'numbers': searchWithError.textToFullSetNumbers(data[key]) })) }, /* Преобразовать одно слово в соединении букв в виде цифр Привет [[п,р,и],[р,и,в],[и,в,е],[в,е,т]] [[1087,1088,1080],[1088,1080,1074],[1080,1074,1077],[1074,1077,1090]] [108710881080, 108810801074, 108010741077, 107410771090] Тоже самое и с 2 буквами и объединяет эти массивы */ 'textToSetNumbers' : (text) => { if (text === '') return new Set(); let characters = text.toLowerCase().split('') //new Set( return new Set( searchWithError.quantityСharacters.map(quantity => Array.from({ 'length' : (text.length + 1 - quantity) }, (_, i) => Number(characters.slice(i, i + quantity) .map(character => character.charCodeAt()) .join('') ) ) ) .flat() ) }, /* Вывести все варианты слова в виде массива Привет [привет, ghbdtn, privet, зкшмук] */ 'textToVariantsText' : (text) => { text = text.toLowerCase() return Array.from(new Set([ text, searchWithError.textRuToEn(text), // бруско => brusko searchWithError.textEnToRu(text), searchWithError.textRuKeyToEn(text), // икгылщ => brusko searchWithError.textEnKeyToRu(text), // hecrj => бруско searchWithError.textRuKeyToEn(searchWithError.textEnKeyToRu(text)), // hecrj => бруско => brusko searchWithError.textRuKeyToEn(searchWithError.textEnToRu(text)) //searchWithError.textEnToRu(searchWithError.textRuKeyToEn(text)) ])) }, /* Преобразовать слово варианты слов, варианты в цифры соединений букв и всё в один сет Привет [привет, ghbdtn, privet, зкшмук] Set(2) { 1, 2 } Set(2) { 1, 2 } Set(2) { 1, 2 } Set(7) { 1, 2, 3, 4, 5, 6, 7 } */ 'textToFullSetNumbers': (text) => { let variantsText = searchWithError.textToVariantsText(text) return variantsText .map(text => searchWithError.textToSetNumbers(text)) .reduce((result, set0) => { set0.forEach((value) => result.add(value)) return result; }, new Set()) }, 'lettersMapping': { 'RuToEn' : { 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'ye', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'h', 'ц': 'c', 'ч': 'ch', 'ш': 'sh', 'щ': 'shch', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya', ' ':' ' }, 'EnToRu': { 'a':'а', 'b':'б', 'c':'ц', 'd':'д', 'e':'е', 'f':'ф', 'g':'г', 'h':'х', 'i':'и', 'j':'ж', 'k':'к', 'l':'л', 'm':'м', 'n':'н', 'o':'о', 'p':'п', 'q':'к', 'r':'р', 's':'с', 't':'т', 'u':'у', 'v':'в', 'w':'в', 'x':'кс', 'y':'й', 'z':'з' }, 'EnKeyToRu' : { 'q':'й', 'w':'ц', 'e':'у', 'r':'к', 't':'е', 'y':'н', 'u':'г', 'i':'ш', 'o':'щ', 'p':'з', '[':'х', ']':'ъ', 'a':'ф', 's':'ы', 'd':'в', 'f':'а', 'g':'п', 'h':'р', 'j':'о', 'k':'л', 'l':'д', ';':'ж', '\'':'э', 'z':'я', 'x':'ч', 'c':'с', 'v':'м', 'b':'и', 'n':'т', 'm':'ь', ',':'б', '.':'ю', ' ':' ' }, 'RuKeyToEn': { ' ':' ', 'а':'f', 'б':',', 'в':'d', 'г':'u', 'д':'l', 'е':'t', 'ж':';', 'з':'p', 'и':'b', 'й':'q', 'к':'r', 'л':'k', 'м':'v', 'н':'y', 'о':'j', 'п':'g', 'р':'h', 'с':'c', 'т':'n', 'у':'e', 'ф':'a', 'х':'[', 'ц':'w', 'ч':'x', 'ш':'i', 'щ':'o', 'ъ':']', 'ы':'s', 'ь':'m', 'э':'\'', 'ю':'.', 'я':'z' }, 'textTo': (text, lettersMapping) => { return text .toLowerCase() .split('') //.filter(character => lettersMapping[character]) .map(character => lettersMapping[character] ? lettersMapping[character] : character) .join('') } }, // бруско => brusko 'textRuToEn': (text) => { return searchWithError.lettersMapping.textTo(text, searchWithError.lettersMapping.RuToEn) }, 'textEnToRu': (text) => { return searchWithError.lettersMapping.textTo(text, searchWithError.lettersMapping.EnToRu) }, // икгылщ => brusko 'textRuKeyToEn' : (text) => { return searchWithError.lettersMapping.textTo(text, searchWithError.lettersMapping.RuKeyToEn) }, // ,hecrj => бруско 'textEnKeyToRu' : (text) => { return searchWithError.lettersMapping.textTo(text, searchWithError.lettersMapping.EnKeyToRu) }, /* Основная функция поиска searchWithError.search('Привет', console.log) [key, key, key] searchWithError.search('Привет', console.log, {'full':true}) [[key: item], [key: item]] */ 'search' : (searchText = '', callback = console.log, options = {}) => { if (searchWithError.data.list.length === 0) { alert(`Нет базы для поиска, воспользуйтесь функцией searchWithError.setDatabase({key:value, key:value}), а после searchWithError.search()`) return; } // Таймер, чтоб не реагировал до тех пор, пока полностью не введут слово clearTimeout(searchWithError.timer) searchWithError.timer = setTimeout(() => { // При очистке поиска очищать результат if(searchText === '' || searchText.length < 3) { searchWithError.handleResponse(callback, [], options.arg) return; } // Текст для поиска переводим в массив let searchNumbers = searchWithError.textToSetNumbers(searchText) let searchNumbersArray = Array.from(searchNumbers) // Подсчёт процента, фильтр, сортировка let result = searchWithError.data.list .map(item => { // Количество доступных совпадений item.quantityСoincidencePossible = searchNumbersArray.length // Количество найденных совпадений item.quantityCoincidenceFound = searchNumbersArray.filter(number => item.numbers.has(number)).length // Процент найденных совпадений // Math.floor( item.percentCoincidence = item.quantityCoincidenceFound * 100 / item.quantityСoincidencePossible return item }) // Оставляем с нормальным процентом .filter(item => item.percentCoincidence >= searchWithError.minProbability) // Сортируем по количеству найденных совпадений .sort((item0, item1) => item0.quantityCoincidenceFound == item1.quantityCoincidenceFound ? 0 : (item0.quantityCoincidenceFound < item1.quantityCoincidenceFound ? 1 : -1) ) // Если задано количество, то оставляем нужное if (options.quantity) result = result .filter((_, index) => index < options.quantity) // Полный вывод в виде масивов [[key: item], [key: item]] if (options.full) result = result.map(item => { item.variants = searchWithError.textToVariantsText(item.value) return [item.key, item] }) else // Обычный вывод в виде масивов ключей [key, key, key] result = result.map(item => item.key) searchWithError.handleResponse(callback, result, options.arg) }, searchWithError.delay) }, // Обработка ответа 'handleResponse' : (callback = console.log, data, arg) => { if (data === undefined) callback() else if (arg === undefined) callback(data) else callback(data, arg) } }