talisman
Version:
Straightforward fuzzy matching, information retrieval and NLP building blocks for JavaScript.
163 lines (128 loc) • 7.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /**
* Talisman phonetics/daitch-mokotoff
* ===================================
*
* The Daitch-Mokotoff Soundex.
*
* [Reference]:
* https://en.wikipedia.org/wiki/Daitch%E2%80%93Mokotoff_Soundex
*/
exports.default = daitchMokotoff;
var _deburr = require('lodash/deburr');
var _deburr2 = _interopRequireDefault(_deburr);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Rules.
*
* [Note]:
* For the (RS|RZ) part, the original algo says (94, 4) but most
* implementations drop it to only (94). This implementation follows the
* original algo.
*/
var MNNM = /^(MN|NM)/,
MN = /^(M|N)/;
var RULES = {
A: [[/^(AI|AJ|AY)/, 0, 1, null], [/^AU/, 0, 7, null], [null, 0, null, null]],
Ą: [[null, null, null, [6, null]]],
B: [[null, 7, 7, 7]],
C: [[/^CHS/, 5, 54, 54], [/^CH/, [5, 4], [5, 4], [5, 4]], [/^CK/, [5, 45], [5, 45], [5, 45]], [/^(CSZ|CZS|CZ|CS)/, 4, 4, 4], [null, [5, 4], [5, 4], [5, 4]]],
D: [[/^(DRZ|DRS|DSH|DSZ|DZH|DZS|DS|DZ)/, 4, 4, 4], [/^(DT|D)/, 3, 3, 3]],
E: [[/^(EI|EJ|EY)/, 0, 1, null], [/^EU/, 1, 1, null], [null, 0, null, null]],
Ę: [[null, null, null, [6, null]]],
F: [[/^(FB|F)/, 7, 7, 7]],
G: [[null, 5, 5, 5]],
H: [[null, 5, 5, null]],
I: [[/^(IA|IE|IO|IU)/, 1, null, null], [null, 0, null, null]],
J: [[null, [1, 4], [null, 4], [null, 4]]],
K: [[/^KS/, 5, 54, 54], [/^(KH|K)/, 5, 5, 5]],
L: [[null, 8, 8, 8]],
M: [[MNNM, null, 66, 66], [MN, 6, 6, 6]],
N: [[MNNM, null, 66, 66], [MN, 6, 6, 6]],
O: [[/^(OI|OJ|OY)/, 0, 1, null], [null, 0, null, null]],
P: [[/^(PF|PH|P)/, 7, 7, 7]],
Q: [[null, 5, 5, 5]],
R: [[/^(RZ|RS)/, [94, 4], [94, 4], [94, 4]], [null, 9, 9, 9]],
S: [[/^(SCHTSCH|SCHTSH|SCHTCH|SHTCH|SHCH|SHTSH)/, 2, 4, 4], [/^SCH/, 4, 4, 4], [/^(SHT|SCHT|SCHD)/, 2, 43, 43], [/^SH/, 4, 4, 4], [/^(STCH|STSCH|SC|STRZ|STRS|STSH)/, 2, 4, 4], [/^ST/, 2, 43, 43], [/^(SZCZ|SZCS)/, 2, 4, 4], [/^(SZT|SHD|SZD|SD)/, 2, 43, 43], [/^(SZ|S)/, 4, 4, 4]],
T: [[/^(TCH|TTCH|TTSCH)/, 4, 4, 4], [/^TH/, 3, 3, 3], [/^(TRZ|TRS|TSCH|TSH|TS|TTS|TTSZ|TC|TZ|TTZ|TZS|TSZ)/, 4, 4, 4], [null, 3, 3, 3]],
Ţ: [[null, [3, 4], [3, 4], [3, 4]]],
U: [[/^(UI|UJ|UY)/, 0, 1, null], [/^(UE|U)/, 0, null, null]],
V: [[null, 7, 7, 7]],
W: [[null, 7, 7, 7]],
X: [[null, 5, 54, 54]],
Y: [[null, 1, null, null]],
Z: [[/^(ZHDZH|ZDZH|ZDZ)/, 2, 4, 4], [/^(ZHD|ZD)/, 2, 43, 43], [/^(ZSCH|ZSH|ZH|ZS|Z)/, 4, 4, 4]]
};
/**
* Helpers.
*/
function pad(code) {
return (code + '000000').slice(0, 6);
}
function permutations(code) {
var codes = [''];
for (var i = 0, l = code.length; i < l; i++) {
var current = code[i];
if ((typeof current === 'undefined' ? 'undefined' : _typeof(current)) === 'object') {
// Doubling the codes
for (var j = 0, m = codes.length; j < m; j++) {
codes.push(codes[j]);
} // Filling the codes
for (var _j = 0, _m = codes.length; _j < _m; _j++) {
var s = current[_j < _m / 2 ? 0 : 1];
codes[_j] += s !== null ? s : '';
}
} else {
for (var _j2 = 0, _m2 = codes.length; _j2 < _m2; _j2++) {
codes[_j2] += current;
}
}
}
return codes;
}
var VOWELS = new Set(['A', 'E', 'I', 'O', 'U', 'Y']);
/**
* Function taking a single name and computing its Daitch-Mokotoff soundex code.
*
* @param {string} name - The name to process.
* @return {string} - The Daitch-Mokotoff code.
*
* @throws {Error} The function expects the name to be a string.
*/
function daitchMokotoff(name) {
if (typeof name !== 'string') throw Error('talisman/phonetics/daitch-mokotoff: the given name is not a string.');
var code = [];
var current = (0, _deburr2.default)(name).toUpperCase().replace(/[^A-ZĄĘŢ]/g, '');
var start = true,
lastPattern = void 0;
// Applying the rules
while (current.length) {
// Find the subset of rules applying to the current letter
var firstLetter = current.charAt(0),
rules = RULES[firstLetter];
for (var i = 0, l = rules.length; i < l; i++) {
var _rules$i = rules[i],
pattern = _rules$i[0],
ifFirstLetter = _rules$i[1],
vowelNext = _rules$i[2],
usual = _rules$i[3];
var match = pattern ? current.match(pattern) : [firstLetter];
if (match) {
var offset = match[0].length;
var correctCode = usual;
if (start) correctCode = ifFirstLetter;else if (VOWELS.has(current.charAt(offset))) correctCode = vowelNext;
if (lastPattern !== pattern && correctCode !== null) code.push(correctCode);
lastPattern = pattern || firstLetter;
current = current.slice(offset);
break;
}
}
start = false;
}
// NOTE: it's probably possible to have better performance here
return Array.from(new Set(permutations(code).map(pad)));
}
module.exports = exports['default'];