UNPKG

talisman

Version:

Straightforward fuzzy matching, information retrieval and NLP building blocks for JavaScript.

249 lines (191 loc) 5.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: false }); exports.default = damerauLevenshtein; exports.limited = limited; /** * Talisman metrics/damerau-levenshtein * ===================================== * * Functions computing the Damerau-Levenshtein distance. * * [Reference]: * https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance * * [Original Code]: * http://blog.softwx.net/2015/01/optimizing-damerau-levenshtein_15.html * * [Tags]: metric, string metric. */ /** * Function returning the Damerau-Levenshtein distance between two sequences. * * @param {mixed} a - The first sequence to process. * @param {mixed} b - The second sequence to process. * @return {number} - The Damerau-Levenshtein distance between a & b. */ function damerauLevenshtein(a, b) { if (a === b) return 0; var la = a.length, lb = b.length; if (!la) return lb; if (!lb) return la; // Swapping the strings so that the shorter string is the first one. if (la > lb) { var _ref = [b, a]; a = _ref[0]; b = _ref[1]; var _ref2 = [lb, la]; la = _ref2[0]; lb = _ref2[1]; } // Ignoring common suffix while (la > 0 && a[la - 1] === b[lb - 1]) { la--; lb--; } var start = 0; // If a common prefix exists of if a is a full b suffix if (a[0] === b[0] || !la) { // Common prefix can also be ignored while (start < la && a[start] === b[start]) { start++; }la -= start; lb -= start; if (!la) return lb; b = b.slice(start, start + lb); } var v0 = new Array(lb), v2 = new Array(lb); for (var i = 0; i < lb; i++) { v0[i] = i + 1; }var charA = a[0], current = 0; // Starting the nested loops for (var _i = 0; _i < la; _i++) { var previousCharA = charA; var nextTranspositionCost = 0, charB = b[0], left = _i; current = _i + 1; charA = a[start + _i]; for (var j = 0; j < lb; j++) { var above = current, previousCharB = charB; var thisTranspositionCost = nextTranspositionCost; charB = b[j]; nextTranspositionCost = v2[j]; v2[j] = current = left; left = v0[j]; if (charA !== charB) { // Insertion if (left < current) current = left; // Deletion if (above < current) current = above; current++; // Transposition if (_i !== 0 && j !== 0 && charA === previousCharB && charB === previousCharA) { thisTranspositionCost++; if (thisTranspositionCost < current) current = thisTranspositionCost; } } v0[j] = current; } } return current; } /** * Function returning the Damerau-Levenshtein distance between two sequences * but with a twist: this version will stop its computation if distance * exceed a given maximum and return Infinity. * * @param {number} max - Maximum distance. * @param {mixed} a - The first sequence to process. * @param {mixed} b - The second sequence to process. * @return {number} - The Damerau-Levenshtein distance between a & b or Infinity. */ function limited(max, a, b) { if (a === b) return 0; var la = a.length, lb = b.length; if (!la) return lb > max ? Infinity : lb; if (!lb) return la > max ? Infinity : la; // Swapping the strings so that the shorter string is the first one. if (la > lb) { var _ref3 = [b, a]; a = _ref3[0]; b = _ref3[1]; var _ref4 = [lb, la]; la = _ref4[0]; lb = _ref4[1]; } // Ignoring common suffix while (la > 0 && a[la - 1] === b[lb - 1]) { la--; lb--; } var start = 0; // If a common prefix exists of if a is a full b suffix if (a[0] === b[0] || !la) { // Common prefix can also be ignored while (start < la && a[start] === b[start]) { start++; }la -= start; lb -= start; if (!la) return lb > max ? Infinity : lb; b = b.slice(start, start + lb); } var diff = lb - la; if (max > lb) max = lb;else if (diff > max) return Infinity; var v0 = new Array(lb), v2 = new Array(lb); var i = void 0; for (i = 0; i < max; i++) { v0[i] = i + 1; }for (; i < lb; i++) { v0[i] = max + 1; }var offset = max - diff, haveMax = max < lb; var jStart = 0, jEnd = max; var charA = a[0], current = 0; // Starting the nested loops for (i = 0; i < la; i++) { var previousCharA = charA; var nextTranspositionCost = 0, charB = b[0], left = i; current = i + 1; charA = a[start + i]; jStart += i > offset ? 1 : 0; jEnd += jEnd < lb ? 1 : 0; for (var j = jStart; j < jEnd; j++) { var above = current, previousCharB = charB; var thisTranspositionCost = nextTranspositionCost; charB = b[j]; nextTranspositionCost = v2[j]; v2[j] = current = left; left = v0[j]; if (charA !== charB) { // Insertion if (left < current) current = left; // Deletion if (above < current) current = above; current++; // Transposition if (i !== 0 && j !== 0 && charA === previousCharB && charB === previousCharA) { thisTranspositionCost++; if (thisTranspositionCost < current) current = thisTranspositionCost; } } v0[j] = current; } if (haveMax && v0[i + diff] > max) return Infinity; } return current <= max ? current : Infinity; } module.exports = exports["default"]; exports["default"].limited = exports.limited;