function normalizeTitle(str) { return str ? str.toLowerCase().replace(/[^a-z0-9]/g, '') : ''; } // Compute match score: exact > startsWith > includes > other function getMatchScore(film, query) { const nTitle = normalizeTitle(film.title); const nQuery = normalizeTitle(query); if (nTitle === nQuery) return 100; if (nTitle.startsWith(nQuery)) return 80; if (nTitle.includes(nQuery)) return 60; return 10; } // Merge and rank by match function mergeResults(arrays, query = '', limit = 5) { const map = {}; arrays.flat().forEach(entry => { const key = normalizeTitle(entry.title) + (entry.year ? '|' + entry.year : ''); if (!map[key]) { map[key] = { title: entry.title, year: entry.year, results: [], __raw: entry // For tie-break }; } map[key].results.push({ source: entry.source, ...entry }); }); let out = Object.values(map); if (query) { out.forEach(f => f.__score = getMatchScore(f, query)); out = out.sort((a, b) => b.__score - a.__score); } // Remove internals, trim to limit return out.slice(0, limit).map(f => { delete f.__score; delete f.__raw; return f; }); } module.exports = { mergeResults };