From 74ad46ff5b1f6ef3dbf7aa270b6453fa9b9f405d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Ara=C3=BAjo?= Date: Thu, 3 Oct 2024 20:22:46 -0300 Subject: [PATCH 1/7] Added levenshtein distance to search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: George Araújo --- assets/js/levenshtein.js | 101 ++++++++++++++++++++++++++ assets/js/script.js | 151 +++++++++++++++++++++++++++++++++++---- 2 files changed, 239 insertions(+), 13 deletions(-) create mode 100644 assets/js/levenshtein.js diff --git a/assets/js/levenshtein.js b/assets/js/levenshtein.js new file mode 100644 index 00000000..9c8e35b0 --- /dev/null +++ b/assets/js/levenshtein.js @@ -0,0 +1,101 @@ +// Levenshtein algorithm by Gustaf Andersson: https://github.com/gustf/js-levenshtein +function _min(d0, d1, d2, bx, ay) +{ + return d0 < d1 || d2 < d1 + ? d0 > d2 + ? d2 + 1 + : d0 + 1 + : bx === ay + ? d1 + : d1 + 1; +} + +export function levenshtein(a, b) +{ + if (a === b) { + return 0; + } + + if (a.length > b.length) { + var tmp = a; + a = b; + b = tmp; + } + + var la = a.length; + var lb = b.length; + + while (la > 0 && (a.charCodeAt(la - 1) === b.charCodeAt(lb - 1))) { + la--; + lb--; + } + + var offset = 0; + + while (offset < la && (a.charCodeAt(offset) === b.charCodeAt(offset))) { + offset++; + } + + la -= offset; + lb -= offset; + + if (la === 0 || lb < 3) { + return lb; + } + + var x = 0; + var y; + var d0; + var d1; + var d2; + var d3; + var dd; + var dy; + var ay; + var bx0; + var bx1; + var bx2; + var bx3; + + var vector = []; + + for (y = 0; y < la; y++) { + vector.push(y + 1); + vector.push(a.charCodeAt(offset + y)); + } + + var len = vector.length - 1; + + for (; x < lb - 3;) { + bx0 = b.charCodeAt(offset + (d0 = x)); + bx1 = b.charCodeAt(offset + (d1 = x + 1)); + bx2 = b.charCodeAt(offset + (d2 = x + 2)); + bx3 = b.charCodeAt(offset + (d3 = x + 3)); + dd = (x += 4); + for (y = 0; y < len; y += 2) { + dy = vector[y]; + ay = vector[y + 1]; + d0 = _min(dy, d0, d1, bx0, ay); + d1 = _min(d0, d1, d2, bx1, ay); + d2 = _min(d1, d2, d3, bx2, ay); + dd = _min(d2, d3, dd, bx3, ay); + vector[y] = dd; + d3 = d2; + d2 = d1; + d1 = d0; + d0 = dy; + } + } + + for (; x < lb;) { + bx0 = b.charCodeAt(offset + (d0 = x)); + dd = ++x; + for (y = 0; y < len; y += 2) { + dy = vector[y]; + vector[y] = dd = _min(dy, d0, dd, bx0, vector[y + 1]); + d0 = dy; + } + } + + return dd; +} diff --git a/assets/js/script.js b/assets/js/script.js index 5806a729..48a6c089 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -1,4 +1,5 @@ import "./dark_mode.js"; +import { levenshtein } from "./levenshtein.js"; const searchInput = document.querySelector("#search-input"); const cardsSection = document.querySelector("#cards"); @@ -52,30 +53,154 @@ function filterCards() { searchCards(); } +function sortCards(sortingArray) { + const list = document.querySelector("#cards"); + + if (listOfCardsFiltered.length > 0) { + if (!Array.isArray(sortingArray) || !sortingArray.length) { + [...list.querySelectorAll(".card")] + // itemsArray.sort((a, b) => sortingArr.indexOf(a) - sortingArr.indexOf(b)); + .sort((a, b) => a.querySelector(".card__title").textContent.toLowerCase().localeCompare(b.querySelector(".card__title").textContent.toLowerCase())) + .forEach(node => list.appendChild(node)); + } else { + [...list.querySelectorAll(".card")] + // itemsArray.sort((a, b) => sortingArr.indexOf(a) - sortingArr.indexOf(b)); + .sort((a, b) => a.querySelector(".card__title").textContent.toLowerCase().localeCompare(b.querySelector(".card__title").textContent.toLowerCase())) + .forEach(node => list.appendChild(node)); + } + } +} + function searchCards() { - const inputValue = searchInput.value.toLowerCase(); - let cardsFiltered = []; + const inputValue = searchInput.value.toLowerCase().trim(); + let cardsScores = []; - for (const card of listOfCardsFiltered) { - const cardContent = card.textContent.toLowerCase(); + if (inputValue.length > 0) { + for (const [i, card] of listOfCardsFiltered.entries()) { + // search for words inside the title that have a levenshtein distance lower or equal to 5 + let cardScore = 0; + const cardTitle = card.querySelector(".card__title").textContent.toLowerCase(); + let titleWords = cardTitle.split(/(\s+)/); + let titleScore = 0; - if (cardContent.includes(inputValue)){ - card.style.display = ""; - cardsFiltered.push(card); + titleWords.forEach((word) => { + if (word.includes(inputValue)) { + cardScore += 10; + } + const levenshteinDistance = levenshtein(word, inputValue); + if ((levenshteinDistance <= 2) && (10 - levenshteinDistance > titleScore)) { + // only the word with the lowest levenshtein distance will be considered + titleScore = 10 - levenshteinDistance; + } + }); + + // give extra points for words in title + cardScore += titleScore * 10; + + // search for words inside the description that have a levenshtein distance lower or equal to 5 + const cardDescription = card.querySelector(".card__description").textContent.toLowerCase(); + let descriptionWords = cardDescription.split(/(\s+)/); + let descriptionScore = 0; + + descriptionWords.forEach((word) => { + const levenshteinDistance = levenshtein(word, inputValue); + if ((levenshteinDistance <= 2) && (10 - levenshteinDistance > descriptionScore)) { + // only the word with the lowest levenshtein distance will be considered + descriptionScore = 10 - levenshteinDistance; + } + }); + + cardScore += descriptionScore; + + if (cardScore > 0) { + card.style.display = ""; + cardsScores.push([card, cardScore]); + // cardsFiltered.push(card); + } else { + card.style.display = "none"; + } + + // const cardContent = card.textContent.toLowerCase(); + + // if (cardContent.includes(inputValue)){ + // card.style.display = ""; + // cardsFiltered.push(card); + // } else { + // card.style.display = "none"; + // } + } + + const msgNotFound = document.querySelector("div.msg"); + + if (cardsScores.length > 0) { + msgNotFound.style.display = "none"; + cardsScores.sort((a, b) => b[1] - a[1]); + // sortCards(cardsScores); } else { - card.style.display = "none"; + msgNotFound.style.display = ""; } + + } else { + for (const card of listOfCardsFiltered) { + card.style.display = ""; + cardsScores.push(card); + } + + const msgNotFound = document.querySelector("div.msg"); + msgNotFound.style.display = "none"; + + // sortCards(); } - const msgNotFound = document.querySelector("div.msg"); - msgNotFound.style.display = cardsFiltered.length==0 ? "" : "none"; - + + // const arr1 = ['d','a','b','c'] ; + // const arr2 = [{a:1},{c:3},{d:4},{b:2}]; + // const sortArray = (arr1, arr2) => { + // arr2.sort((a, b) => { + // const aKey = Object.keys(a)[0]; + // const bKey = Object.keys(b)[0]; + // return arr1.indexOf(aKey) - arr1.indexOf(bKey); + // }); + // }; + // sortArray(arr1, arr2); + // console.log(arr2); + + + // console.log(listOfCardsFiltered[0].querySelector(".card__title").textContent); + // console.log(listOfCardsFiltered[0].querySelector(".card__description").textContent); + // console.log(levenshtein("abstracao", "Abstração")); + // console.log(levenshtein("cabeça", "Abstração")); + // console.log(levenshtein("Abstração", "Abstração")); + // console.log(levenshtein("chuazenger", "Schwarzenegger")); + + + + // let search_results = listOfCardsFiltered + // .filter(prof => { + // // Filter results by doing case insensitive match on name here + // return prof.name.toLowerCase().includes(keyword.toLowerCase()); + // }) + // .sort((a, b) => { + // // Sort results by matching name with keyword position in name + // if(a.name.toLowerCase().indexOf(keyword.toLowerCase()) > b.name.toLowerCase().indexOf(keyword.toLowerCase())) { + // return 1; + // } else if (a.name.toLowerCase().indexOf(keyword.toLowerCase()) < b.name.toLowerCase().indexOf(keyword.toLowerCase())) { + // return -1; + // } else { + // if(a.name > b.name) + // return 1; + // else + // return -1; + // } + // }); + + // console.log(search_results); } function insertCardsIntoHtml(data) { let cards = `
@@ -228,4 +353,4 @@ function generateContentId(title = '', description = '', hash = 5381) { const hashString = Math.abs(hash).toString(36); // Convert to base-36 string return hashString; -} \ No newline at end of file +} From 0f2c0c5e8c721e4eaeca95323fa515acd048e813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Ara=C3=BAjo?= Date: Thu, 3 Oct 2024 21:04:47 -0300 Subject: [PATCH 2/7] Fixed search and sort functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: George Araújo --- assets/js/script.js | 109 +++++++++++++------------------------------- 1 file changed, 31 insertions(+), 78 deletions(-) diff --git a/assets/js/script.js b/assets/js/script.js index 48a6c089..1cee7376 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -54,19 +54,21 @@ function filterCards() { } function sortCards(sortingArray) { - const list = document.querySelector("#cards"); - if (listOfCardsFiltered.length > 0) { if (!Array.isArray(sortingArray) || !sortingArray.length) { - [...list.querySelectorAll(".card")] - // itemsArray.sort((a, b) => sortingArr.indexOf(a) - sortingArr.indexOf(b)); + const cards = document.querySelector("#cards"); + // selects all cards that are not hidden and sorts them by title + // every child is re-appended to cards in the order of the now sorted array. When an element is re-appended it is actually moved from its previous location + [...cards.querySelectorAll(".card:not([style*='display: none;'])")] .sort((a, b) => a.querySelector(".card__title").textContent.toLowerCase().localeCompare(b.querySelector(".card__title").textContent.toLowerCase())) - .forEach(node => list.appendChild(node)); + .forEach(node => cards.appendChild(node)); } else { - [...list.querySelectorAll(".card")] - // itemsArray.sort((a, b) => sortingArr.indexOf(a) - sortingArr.indexOf(b)); - .sort((a, b) => a.querySelector(".card__title").textContent.toLowerCase().localeCompare(b.querySelector(".card__title").textContent.toLowerCase())) - .forEach(node => list.appendChild(node)); + const cards = document.querySelector("#cards"); + // selects all cards that are not hidden and sorts them by the order of the sortingArray + // every child is re-appended to cards in the order of the now sorted array. When an element is re-appended it is actually moved from its previous location + [...cards.querySelectorAll(".card:not([style*='display: none;'])")] + .sort((a, b) => sortingArray.indexOf(a) - sortingArray.indexOf(b)) + .forEach(node => cards.appendChild(node)); } } } @@ -76,37 +78,39 @@ function searchCards() { let cardsScores = []; if (inputValue.length > 0) { - for (const [i, card] of listOfCardsFiltered.entries()) { - // search for words inside the title that have a levenshtein distance lower or equal to 5 + for (const card of listOfCardsFiltered) { let cardScore = 0; + + // search for words inside the title that either contain the input value or have a levenshtein distance lower or equal to 2 const cardTitle = card.querySelector(".card__title").textContent.toLowerCase(); let titleWords = cardTitle.split(/(\s+)/); let titleScore = 0; titleWords.forEach((word) => { - if (word.includes(inputValue)) { - cardScore += 10; - } const levenshteinDistance = levenshtein(word, inputValue); - if ((levenshteinDistance <= 2) && (10 - levenshteinDistance > titleScore)) { - // only the word with the lowest levenshtein distance will be considered - titleScore = 10 - levenshteinDistance; + if (word.includes(inputValue) || ((inputValue.length > 3) && (levenshteinDistance <= 2))) { + if (10 - levenshteinDistance > titleScore) { + // only the word with the lowest levenshtein distance will be considered + titleScore = 10 - levenshteinDistance; + } } }); // give extra points for words in title cardScore += titleScore * 10; - // search for words inside the description that have a levenshtein distance lower or equal to 5 + // search for words inside the description that either contain the input value or have a levenshtein distance lower or equal to 2 const cardDescription = card.querySelector(".card__description").textContent.toLowerCase(); let descriptionWords = cardDescription.split(/(\s+)/); let descriptionScore = 0; descriptionWords.forEach((word) => { const levenshteinDistance = levenshtein(word, inputValue); - if ((levenshteinDistance <= 2) && (10 - levenshteinDistance > descriptionScore)) { - // only the word with the lowest levenshtein distance will be considered - descriptionScore = 10 - levenshteinDistance; + if (word.includes(inputValue) || ((inputValue.length > 3) && (levenshteinDistance <= 2))) { + if (10 - levenshteinDistance > descriptionScore) { + // only the word with the lowest levenshtein distance will be considered + descriptionScore = 10 - levenshteinDistance; + } } }); @@ -115,32 +119,26 @@ function searchCards() { if (cardScore > 0) { card.style.display = ""; cardsScores.push([card, cardScore]); - // cardsFiltered.push(card); } else { card.style.display = "none"; } - - // const cardContent = card.textContent.toLowerCase(); - - // if (cardContent.includes(inputValue)){ - // card.style.display = ""; - // cardsFiltered.push(card); - // } else { - // card.style.display = "none"; - // } } const msgNotFound = document.querySelector("div.msg"); if (cardsScores.length > 0) { msgNotFound.style.display = "none"; + // sort cards by score cardsScores.sort((a, b) => b[1] - a[1]); - // sortCards(cardsScores); + // remove the scores from the array + cardsScores = cardsScores.map((card) => card[0]); + sortCards(cardsScores); } else { msgNotFound.style.display = ""; } } else { + // display all cards if search input is empty for (const card of listOfCardsFiltered) { card.style.display = ""; cardsScores.push(card); @@ -148,53 +146,8 @@ function searchCards() { const msgNotFound = document.querySelector("div.msg"); msgNotFound.style.display = "none"; - - // sortCards(); + sortCards(); } - - - // const arr1 = ['d','a','b','c'] ; - // const arr2 = [{a:1},{c:3},{d:4},{b:2}]; - // const sortArray = (arr1, arr2) => { - // arr2.sort((a, b) => { - // const aKey = Object.keys(a)[0]; - // const bKey = Object.keys(b)[0]; - // return arr1.indexOf(aKey) - arr1.indexOf(bKey); - // }); - // }; - // sortArray(arr1, arr2); - // console.log(arr2); - - - // console.log(listOfCardsFiltered[0].querySelector(".card__title").textContent); - // console.log(listOfCardsFiltered[0].querySelector(".card__description").textContent); - // console.log(levenshtein("abstracao", "Abstração")); - // console.log(levenshtein("cabeça", "Abstração")); - // console.log(levenshtein("Abstração", "Abstração")); - // console.log(levenshtein("chuazenger", "Schwarzenegger")); - - - - // let search_results = listOfCardsFiltered - // .filter(prof => { - // // Filter results by doing case insensitive match on name here - // return prof.name.toLowerCase().includes(keyword.toLowerCase()); - // }) - // .sort((a, b) => { - // // Sort results by matching name with keyword position in name - // if(a.name.toLowerCase().indexOf(keyword.toLowerCase()) > b.name.toLowerCase().indexOf(keyword.toLowerCase())) { - // return 1; - // } else if (a.name.toLowerCase().indexOf(keyword.toLowerCase()) < b.name.toLowerCase().indexOf(keyword.toLowerCase())) { - // return -1; - // } else { - // if(a.name > b.name) - // return 1; - // else - // return -1; - // } - // }); - - // console.log(search_results); } function insertCardsIntoHtml(data) { From 0305b2b99ab993879b8012948d8c542c034b9bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Ara=C3=BAjo?= Date: Thu, 3 Oct 2024 21:56:39 -0300 Subject: [PATCH 3/7] Giving higher score when search string is substring of title or description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: George Araújo --- assets/js/script.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/assets/js/script.js b/assets/js/script.js index 1cee7376..7125d79d 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -88,7 +88,9 @@ function searchCards() { titleWords.forEach((word) => { const levenshteinDistance = levenshtein(word, inputValue); - if (word.includes(inputValue) || ((inputValue.length > 3) && (levenshteinDistance <= 2))) { + if (word.includes(inputValue)) { + titleScore = 10; + } else if ((inputValue.length > 3) && (levenshteinDistance <= 2)) { if (10 - levenshteinDistance > titleScore) { // only the word with the lowest levenshtein distance will be considered titleScore = 10 - levenshteinDistance; @@ -106,7 +108,9 @@ function searchCards() { descriptionWords.forEach((word) => { const levenshteinDistance = levenshtein(word, inputValue); - if (word.includes(inputValue) || ((inputValue.length > 3) && (levenshteinDistance <= 2))) { + if (word.includes(inputValue)) { + descriptionScore = 10; + } else if ((inputValue.length > 3) && (levenshteinDistance <= 2)) { if (10 - levenshteinDistance > descriptionScore) { // only the word with the lowest levenshtein distance will be considered descriptionScore = 10 - levenshteinDistance; @@ -128,7 +132,7 @@ function searchCards() { if (cardsScores.length > 0) { msgNotFound.style.display = "none"; - // sort cards by score + // sort the array of cards by score cardsScores.sort((a, b) => b[1] - a[1]); // remove the scores from the array cardsScores = cardsScores.map((card) => card[0]); From 94d96e895d09aac8768eeb948df370909e049aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Ara=C3=BAjo?= Date: Thu, 3 Oct 2024 22:01:12 -0300 Subject: [PATCH 4/7] Increased threshold of levenshtein distance to consider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: George Araújo --- assets/js/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/script.js b/assets/js/script.js index 7125d79d..ff599128 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -90,7 +90,7 @@ function searchCards() { const levenshteinDistance = levenshtein(word, inputValue); if (word.includes(inputValue)) { titleScore = 10; - } else if ((inputValue.length > 3) && (levenshteinDistance <= 2)) { + } else if ((inputValue.length > 3) && (levenshteinDistance <= 3)) { if (10 - levenshteinDistance > titleScore) { // only the word with the lowest levenshtein distance will be considered titleScore = 10 - levenshteinDistance; @@ -110,7 +110,7 @@ function searchCards() { const levenshteinDistance = levenshtein(word, inputValue); if (word.includes(inputValue)) { descriptionScore = 10; - } else if ((inputValue.length > 3) && (levenshteinDistance <= 2)) { + } else if ((inputValue.length > 3) && (levenshteinDistance <= 3)) { if (10 - levenshteinDistance > descriptionScore) { // only the word with the lowest levenshtein distance will be considered descriptionScore = 10 - levenshteinDistance; From 34fb377b377a045d3604e86f78bd6764f0e4f377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Ara=C3=BAjo?= Date: Thu, 3 Oct 2024 22:09:30 -0300 Subject: [PATCH 5/7] Fixed weird indentation in script.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: George Araújo --- assets/js/script.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/js/script.js b/assets/js/script.js index ff599128..1abb8f97 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -302,12 +302,12 @@ function generateCardId(defaultCardId, title, description) { * @returns {string} The hashed representation of the content. */ function generateContentId(title = '', description = '', hash = 5381) { - const data = (title + description).slice(0, 32).split(' ').join('') + const data = (title + description).slice(0, 32).split(' ').join('') - for (let i = 0; i < data.length; i++) { - hash = ((hash << 5) + hash) + data.charCodeAt(i); - } + for (let i = 0; i < data.length; i++) { + hash = ((hash << 5) + hash) + data.charCodeAt(i); + } - const hashString = Math.abs(hash).toString(36); // Convert to base-36 string - return hashString; + const hashString = Math.abs(hash).toString(36); // Convert to base-36 string + return hashString; } From bab7bee69c99c6bda69ef6467130787ef94e66df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Ara=C3=BAjo?= Date: Fri, 4 Oct 2024 15:33:40 -0300 Subject: [PATCH 6/7] Fixed split bug, now support individual search strings, breaking loop after exact match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: George Araújo --- assets/js/script.js | 84 +++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/assets/js/script.js b/assets/js/script.js index 1abb8f97..df109693 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -1,11 +1,17 @@ import "./dark_mode.js"; import { levenshtein } from "./levenshtein.js"; +const exactWordScore = 11; +const partialWordScore = 10; +const levenshteinScore = 10; +const levenshteinThreshold = 3; + const searchInput = document.querySelector("#search-input"); const cardsSection = document.querySelector("#cards"); const filterSelect = document.querySelector("#tags-filter"); let listOfCardsFiltered = []; let favoriteCards = []; + const starIcon = "https://img.icons8.com/ios/50/star--v1.png"; const starIconFilled = "https://img.icons8.com/ios-glyphs/30/ffe100/star--v1.png"; @@ -78,44 +84,78 @@ function searchCards() { let cardsScores = []; if (inputValue.length > 0) { + const searchWords = inputValue.split(/\s+/); + for (const card of listOfCardsFiltered) { let cardScore = 0; - // search for words inside the title that either contain the input value or have a levenshtein distance lower or equal to 2 + // search for words inside the title that either contains the search words or have a low levenshtein distance + // only consider the best case for each search word const cardTitle = card.querySelector(".card__title").textContent.toLowerCase(); - let titleWords = cardTitle.split(/(\s+)/); + const titleWords = cardTitle.split(/\s+/); let titleScore = 0; - titleWords.forEach((word) => { - const levenshteinDistance = levenshtein(word, inputValue); - if (word.includes(inputValue)) { - titleScore = 10; - } else if ((inputValue.length > 3) && (levenshteinDistance <= 3)) { - if (10 - levenshteinDistance > titleScore) { - // only the word with the lowest levenshtein distance will be considered - titleScore = 10 - levenshteinDistance; + searchWords.forEach((searchWord) => { + let wordScore = 0; + + titleWords.some((word) => { + if (word == searchWord) { + // breaks the loop if the word is an exact match, since no other word can have a higher score + wordScore = exactWordScore; + return true; + + } else if (wordScore < partialWordScore) { + if (word.includes(searchWord)) { + wordScore = partialWordScore; + + } else if (word.length > 3) { + const levenshteinDistance = levenshtein(searchWord, word); + + // only the word with the lowest levenshtein distance will be considered + if ((levenshteinDistance <= levenshteinThreshold) && (levenshteinScore - levenshteinDistance > wordScore)) { + wordScore = levenshteinScore - levenshteinDistance; + } + } } - } + }); + + titleScore += wordScore; }); // give extra points for words in title cardScore += titleScore * 10; - // search for words inside the description that either contain the input value or have a levenshtein distance lower or equal to 2 + // search for words inside the description that either contains the search words or have a low levenshtein distance + // only consider the best case for each search word const cardDescription = card.querySelector(".card__description").textContent.toLowerCase(); - let descriptionWords = cardDescription.split(/(\s+)/); + const descriptionWords = cardDescription.split(/\s+/); let descriptionScore = 0; - descriptionWords.forEach((word) => { - const levenshteinDistance = levenshtein(word, inputValue); - if (word.includes(inputValue)) { - descriptionScore = 10; - } else if ((inputValue.length > 3) && (levenshteinDistance <= 3)) { - if (10 - levenshteinDistance > descriptionScore) { - // only the word with the lowest levenshtein distance will be considered - descriptionScore = 10 - levenshteinDistance; + searchWords.forEach((searchWord) => { + let wordScore = 0; + + descriptionWords.some((word) => { + if (word == searchWord) { + // breaks the loop if the word is an exact match, since no other word can have a higher score + wordScore = exactWordScore; + return true; + + } else if (wordScore < partialWordScore) { + if (word.includes(searchWord)) { + wordScore = partialWordScore; + + } else if (word.length > 3) { + const levenshteinDistance = levenshtein(searchWord, word); + + // only the word with the lowest levenshtein distance will be considered + if ((levenshteinDistance <= levenshteinThreshold) && (levenshteinScore - levenshteinDistance > wordScore)) { + wordScore = levenshteinScore - levenshteinDistance; + } + } } - } + }); + + descriptionScore += wordScore; }); cardScore += descriptionScore; From 82e8b5c817bf7f689e4e5908898729ab0cdf03d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Ara=C3=BAjo?= Date: Sat, 5 Oct 2024 11:12:09 -0300 Subject: [PATCH 7/7] Increased exact word score MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: George Araújo --- assets/js/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/script.js b/assets/js/script.js index df109693..ee41d572 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -1,7 +1,7 @@ import "./dark_mode.js"; import { levenshtein } from "./levenshtein.js"; -const exactWordScore = 11; +const exactWordScore = 12; const partialWordScore = 10; const levenshteinScore = 10; const levenshteinThreshold = 3;