Files
hope-events/frontend/src/lib/fuzzyMatch.ts
T
joshua 3d381944d2 Initial commit
Next.js + Express event management app for Hope Family Church.
2026-07-23 15:26:47 +02:00

51 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** Levenshtein edit distance between two strings */
export function editDistance(a: string, b: string): number {
const m = a.length, n = b.length;
const dp: number[] = Array(n + 1).fill(0).map((_, j) => j);
for (let i = 1; i <= m; i++) {
let prev = dp[0];
dp[0] = i;
for (let j = 1; j <= n; j++) {
const temp = dp[j];
dp[j] = a[i - 1] === b[j - 1] ? prev : 1 + Math.min(prev, dp[j], dp[j - 1]);
prev = temp;
}
}
return dp[n];
}
/** Return a score 01 for how well a user matches a search query */
export function scoreUser(u: any, raw: string): number {
const q = raw.toLowerCase().trim();
if (!q || q.length < 2) return 0;
const name = (u.name || "").toLowerCase();
const email = (u.email || "").toLowerCase();
const phone = (u.phoneNumber || "").replace(/\D/g, "");
const qPhone = q.replace(/\D/g, "");
let best = 0;
// Exact substring matches — highest priority
if (name.includes(q)) best = Math.max(best, 1.0);
if (email.includes(q)) best = Math.max(best, 0.95);
if (qPhone.length >= 3 && phone.includes(qPhone)) best = Math.max(best, 0.95);
// Word-level starts-with (handles partial first/last name)
const words = name.split(/\s+/);
if (words.some((w: string) => w.startsWith(q))) best = Math.max(best, 0.85);
// Fuzzy edit-distance against each name word
const maxDist = q.length <= 4 ? 1 : q.length <= 7 ? 2 : 3;
for (const w of words) {
const slice = w.slice(0, q.length + 2); // compare against similar-length slice
const d = editDistance(q, slice);
if (d <= maxDist) best = Math.max(best, 0.75 - d * 0.15);
}
// Also fuzzy against the full name (catches "jhn smth" → "john smith")
const nameSlice = name.slice(0, q.length + 4);
const fullDist = editDistance(q, nameSlice);
if (fullDist <= maxDist + 1) best = Math.max(best, 0.6 - fullDist * 0.12);
return best;
}