@sap/cds-dk
Version:
Command line client and development toolkit for the SAP Cloud Application Programming Model
91 lines (67 loc) • 2.23 kB
JavaScript
const cache = new Map();
/**
* Levenshtein distance algorithm using recursive calls and cache
*
* @param {string} input - search the list for a best match for this string
* @param {string[]} list - a list of strings to match input against
* @param {null | (s:string) => unknown} log logging method to use, might be null if no logging is wanted
* @param {{maxListLength?: number, maxInputLength?: number}} [options] - optional parameters to control the search
* @returns {string[]} array with best matches, is never null but might be empty in case no search was possible
*/
module.exports = (input, list, log, options = {}) => {
const { maxListLength, maxInputLength } = {...{ maxListLength: 50, maxInputLength: 50 }, ...options};
let minDistWords = [];
if (input.length > maxInputLength || list.length > maxListLength) {
return minDistWords;
}
let minDist = Number.MAX_SAFE_INTEGER;
log && log('\nword\t\tlevDist\t\ttime(ms)');
let runtime = 0;
for (const word of list) {
const start = log && Date.now();
const levDist = levDistance(input, word);
if (log) {
const duration = Date.now() - start;
runtime = runtime + duration;
log(`${word}\t\t${levDist}\t\t${duration}`);
}
if (levDist === minDist) {
minDistWords.push(word);
}
if (levDist < minDist) {
minDist = levDist;
minDistWords = [word];
}
}
log && log(`runtime: ${runtime}ms`);
return minDistWords.sort();
}
const levDistance = (a, b) => {
const cachedObj = cache.get(a)?.get(b);
if (cachedObj) {
return cachedObj;
}
if (a.length === 0) {
return addToCache(a, b, b.length);
}
if (b.length === 0) {
return addToCache(a, b, a.length);
}
const tail_a = a.substring(1);
const tail_b = b.substring(1);
if (a[0] === b[0]) {
return levDistance(tail_a, tail_b);
}
const lev1 = levDistance(tail_a, b);
const lev2 = levDistance(a, tail_b);
const lev3 = levDistance(tail_a, tail_b);
const levDist = Math.min(lev1, lev2, lev3) + 1;
return addToCache(a, b, levDist);
}
const addToCache = (a, b, value) => {
if (!cache.has(a)) {
cache.set(a, new Map());
}
cache.get(a).set(b, value);
return value;
}