UNPKG

fuzzy-name-match

Version:
150 lines (149 loc) 5.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.nameMatchResult = void 0; const defaultConfig = { firstNameWeightage: 70, reverseScoreThreshold: 90, nameMatchThreshold: 80, }; const checkEmpty = (value) => { if (value === null || value === undefined) { return true; } if (typeof value === 'string' || Array.isArray(value)) { return value.length === 0; } return false; }; const similarText = (x, y) => { const length = (string1, string2) => { const length1 = string1.length; const length2 = string2.length; const dp = Array.from({ length: length1 + 1 }, () => Array(length2 + 1).fill(0)); for (let i = 1; i <= length1; i++) { for (let j = 1; j <= length2; j++) { if (string1[i - 1] === string2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[length1][length2]; }; const longestCommonSubsequentLength = length(x.toLowerCase(), y.toLowerCase()); return ((longestCommonSubsequentLength * 2) / (x.length + y.length)) * 100; }; function compareTokens(token1, token2) { if (!token1 || !token2) return 0; const t1 = token1.toLowerCase(); const t2 = token2.toLowerCase(); // Exact match if (t1 === t2) return 100; // Initial vs full name (e.g. "K" vs "Kumar") if (t1.length === 1 && t2.startsWith(t1)) return 85; if (t2.length === 1 && t1.startsWith(t2)) return 85; // Fallback to normal similarity return similarText(t1, t2); } const permute = (arr) => { if (arr.length <= 1) return [arr]; const result = []; arr.forEach((word, idx) => { const remaining = [...arr.slice(0, idx), ...arr.slice(idx + 1)]; for (const perm of permute(remaining)) { result.push([word, ...perm]); } }); return result; }; const nameMatchResult = (name1, name2, config = {}) => { const { firstNameWeightage, reverseScoreThreshold, nameMatchThreshold } = { ...defaultConfig, ...config, }; if (checkEmpty(name1) || checkEmpty(name2)) { return { result: false, score: 0, name_1: name1, name_2: name2, normalised_name_1: "", normalised_name_2: "" }; } const prefix = /^(shri |shri\. |sh\. |smt |smt\. |m\/s |m\/s\. |mrs |mrs\. |mr |mr\. |miss |miss\. |ms |ms\. )/i; const filteredname1 = name1.toLowerCase().replace(prefix, "").trim(); const filteredname2 = name2.toLowerCase().replace(prefix, "").trim(); const fullname1 = filteredname1.split(" "); const fullname2 = filteredname2.split(" "); let nameMatchScore; let result; if (fullname1.length <= 2 && fullname2.length <= 2) { const firstname1 = fullname1[0] ?? ""; const lastname1 = fullname1.slice(1).join(" ") ?? ""; const firstName2 = fullname2[0] ?? ""; const lastName2 = fullname2.slice(1).join(" ") ?? ""; let reverseCheckScore; let firstLastNameScore; let lastFirstNameScore; const firstnameScore = compareTokens(firstname1, firstName2); if (!checkEmpty(lastname1) && !checkEmpty(lastName2)) { const lastnameScore = compareTokens(lastname1, lastName2); nameMatchScore = ((firstnameScore * firstNameWeightage) + (lastnameScore * (100 - firstNameWeightage))) / 100; if (firstname1.length == lastName2.length) { firstLastNameScore = compareTokens(firstname1, lastName2); } else { firstLastNameScore = 0; } if (lastname1.length == firstName2.length) { lastFirstNameScore = compareTokens(lastname1, firstName2); } else { lastFirstNameScore = 0; } if ((firstLastNameScore >= 90) && (lastFirstNameScore >= 90)) { reverseCheckScore = (firstLastNameScore + lastFirstNameScore) / 2; } else { reverseCheckScore = 0; } } else { nameMatchScore = firstnameScore; reverseCheckScore = firstnameScore; } nameMatchScore = reverseCheckScore >= reverseScoreThreshold ? reverseCheckScore : nameMatchScore; } else { const permutations = permute(fullname1); let bestScore = 0; for (const perm of permutations) { const permStr = perm.join(" "); const score = compareTokens(permStr, filteredname2); if (score > bestScore) { bestScore = score; } if (bestScore === 100) break; // perfect match → early exit } nameMatchScore = bestScore; } result = Number(nameMatchScore.toFixed(2)) >= nameMatchThreshold; return { result, score: Number(nameMatchScore.toFixed(2)), name_1: name1, name_2: name2, normalised_name_1: filteredname1, normalised_name_2: filteredname2, }; }; exports.nameMatchResult = nameMatchResult;