/** * The MIT License (MIT) * * Copyright (c) 2015 Jordan Thomas * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ "use strict"; /** * Returns the similarity of the given strings. * * @param a {string} a string * @param b {string} a string * @param caseSensitive {boolean} whether the calculation should be case-sensitive * @returns {number} the similarity of the given strings */ export const jaroWinkler = function (a, b, caseSensitive = true) { // Convert to lowercase if not case-sensitive if (!caseSensitive) { a = a.toLowerCase(); b = b.toLowerCase(); } if (a.length === 0 || b.length === 0) return 0; if (a === b) return 1; // Calculate difference const range = Math.floor(Math.max(a.length, b.length) / 2) - 1; const aMatches = []; const bMatches = []; let low; let high; let m = 0; // Find matches for (let i = 0; i < a.length; i++) { low = (i >= range) ? i - range : 0; high = (i + range <= b.length) ? (i + range) : (b.length - 1); for (let j = low; j <= high; j++) { if (aMatches[i] !== true && bMatches[j] !== true && a[i] === b[j]) { aMatches[i] = true; bMatches[j] = true; m++; break; } } } // Short-circuit if not matches found if (m === 0) return 0; // Count transpositions let k = 0; let numTrans = 0; for (let i = 0; i < a.length; i++) { if (aMatches[i] === true) { let j; for (j = k; j < b.length; j++) { if (bMatches[j] === true) { k = j + 1; break; } } if (a[i] !== b[j]) numTrans++; } } let weight = (m / a.length + m / b.length + (m - numTrans / 2) / m) / 3; let l = 0; const p = 0.1; if (weight > 0.7) { while (a[l] === b[l] && l < 4) l++; weight += l * p * (1 - weight); } return weight; };