natural 6.11.1 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/.env +9 -7
  2. package/.eslintrc.json +13 -0
  3. package/.github/workflows/node.js.yml +57 -19
  4. package/lib/natural/analyzers/SenType.ts +30 -0
  5. package/lib/natural/analyzers/index.d.ts +6 -10
  6. package/lib/natural/brill_pos_tagger/index.d.ts +23 -14
  7. package/lib/natural/brill_pos_tagger/index.js +1 -1
  8. package/lib/natural/classifiers/bayes_classifier.js +6 -1
  9. package/lib/natural/classifiers/classifier.js +19 -32
  10. package/lib/natural/classifiers/classifier_train_parallel.js +7 -5
  11. package/lib/natural/classifiers/index.d.ts +36 -15
  12. package/lib/natural/classifiers/logistic_regression_classifier.js +7 -2
  13. package/lib/natural/classifiers/maxent/Classifier.js +5 -3
  14. package/lib/natural/distance/index.d.ts +11 -14
  15. package/lib/natural/inflectors/count_inflector.js +18 -21
  16. package/lib/natural/inflectors/fr/count_inflector.js +18 -19
  17. package/lib/natural/inflectors/index.d.ts +12 -3
  18. package/lib/natural/inflectors/index.js +3 -0
  19. package/lib/natural/ngrams/index.d.ts +5 -5
  20. package/lib/natural/normalizers/index.d.ts +24 -1
  21. package/lib/natural/normalizers/index.js +4 -1
  22. package/lib/natural/normalizers/normalizer_ja.js +109 -72
  23. package/lib/natural/phonetics/dm_soundex.js +52 -51
  24. package/lib/natural/phonetics/double_metaphone.js +397 -399
  25. package/lib/natural/phonetics/index.d.ts +42 -12
  26. package/lib/natural/phonetics/metaphone.js +134 -157
  27. package/lib/natural/phonetics/phonetic.js +5 -29
  28. package/lib/natural/phonetics/soundex.js +45 -54
  29. package/lib/natural/sentiment/index.d.ts +6 -6
  30. package/lib/natural/spellcheck/index.d.ts +2 -0
  31. package/lib/natural/stemmers/index.d.ts +43 -1
  32. package/lib/natural/stemmers/index.js +1 -0
  33. package/lib/natural/stemmers/token.js +131 -132
  34. package/lib/natural/tfidf/index.d.ts +7 -7
  35. package/lib/natural/tfidf/tfidf.js +7 -1
  36. package/lib/natural/tokenizers/index.d.ts +2 -2
  37. package/lib/natural/trie/index.d.ts +1 -1
  38. package/lib/natural/util/index.d.ts +8 -0
  39. package/lib/natural/util/index.js +2 -0
  40. package/lib/natural/util/storage/File.js +22 -0
  41. package/lib/natural/util/storage/Memcached.js +22 -0
  42. package/lib/natural/util/storage/MongoDB.js +31 -3
  43. package/lib/natural/util/storage/Postgres.js +49 -32
  44. package/lib/natural/util/storage/Redis.js +22 -0
  45. package/lib/natural/util/storage/StorageBackend.js +23 -0
  46. package/lib/natural/util/storage/index.d.ts +39 -0
  47. package/lib/natural/util/storage/index.js +24 -0
  48. package/package.json +22 -9
  49. package/ts_spec/MaxEntClassifier_spec.ts +130 -0
  50. package/ts_spec/SentimentAnalyzer_spec.ts +232 -0
  51. package/ts_spec/WordPunctTokenizer_spec.ts +57 -0
  52. package/ts_spec/aggressive_tokenizer_de_spec.ts +36 -0
  53. package/ts_spec/aggressive_tokenizer_es_spec.ts +39 -0
  54. package/ts_spec/aggressive_tokenizer_fr_spec.ts +111 -0
  55. package/ts_spec/aggressive_tokenizer_hi_spec.ts +70 -0
  56. package/ts_spec/aggressive_tokenizer_nl_spec.ts +39 -0
  57. package/ts_spec/aggressive_tokenizer_pt_spec.ts +48 -0
  58. package/ts_spec/aggressive_tokenizer_spec.ts +57 -0
  59. package/ts_spec/aggressive_tokenizer_sv_spec.ts +52 -0
  60. package/ts_spec/aggressive_tokenizer_vi_spec.ts +48 -0
  61. package/ts_spec/bayes_classifier_spec.ts +165 -0
  62. package/ts_spec/brill_pos_tagger_spec.ts +79 -0
  63. package/ts_spec/brill_pos_trainer_spec.ts +107 -0
  64. package/ts_spec/carry_stemmer_fr_spec.ts +97 -0
  65. package/ts_spec/classifier_spec.ts +104 -0
  66. package/ts_spec/count_inflector_fr_spec.ts +47 -0
  67. package/ts_spec/count_inflector_spec.ts +76 -0
  68. package/ts_spec/damerau_levenshtein_spec.ts +53 -0
  69. package/ts_spec/dice_coefficient_spec.ts +37 -0
  70. package/ts_spec/double_metaphone_spec.ts +739 -0
  71. package/ts_spec/hamming_distance_spec.ts +66 -0
  72. package/ts_spec/jaro-winkler_spec.ts +79 -0
  73. package/ts_spec/lancaster_stemmer_spec.ts +63 -0
  74. package/ts_spec/levenshtein_spec.ts +107 -0
  75. package/ts_spec/logistic_regression_classifier_spec.ts +93 -0
  76. package/ts_spec/longest_path_tree_spec.ts +111 -0
  77. package/ts_spec/metaphone_spec.ts +258 -0
  78. package/ts_spec/ngram_spec.ts +145 -0
  79. package/ts_spec/ngram_zh_spec.ts +124 -0
  80. package/ts_spec/normalizer_ja_spec.ts +165 -0
  81. package/ts_spec/normalizer_no_spec.ts +112 -0
  82. package/ts_spec/normalizer_spec.ts +141 -0
  83. package/ts_spec/noun_inflector_fr_spec.ts +472 -0
  84. package/ts_spec/noun_inflector_ja_spec.ts +116 -0
  85. package/ts_spec/noun_inflector_spec.ts +369 -0
  86. package/ts_spec/orthography_tokenizer_spec.ts +47 -0
  87. package/ts_spec/porter_stemmer_de_spec.ts +53 -0
  88. package/ts_spec/porter_stemmer_es_spec.ts +63 -0
  89. package/ts_spec/porter_stemmer_fr_spec.ts +103 -0
  90. package/ts_spec/porter_stemmer_it_spec.ts +56 -0
  91. package/ts_spec/porter_stemmer_nl_spec.ts +73 -0
  92. package/ts_spec/porter_stemmer_no_spec.ts +148 -0
  93. package/ts_spec/porter_stemmer_pt_spec.ts +50 -0
  94. package/ts_spec/porter_stemmer_ru_spec.ts +67 -0
  95. package/ts_spec/porter_stemmer_spec.ts +202 -0
  96. package/ts_spec/porter_stemmer_sv_spec.ts +41 -0
  97. package/ts_spec/present_verb_inflector_spec.ts +127 -0
  98. package/ts_spec/remove_diacritics_spec.ts +30 -0
  99. package/ts_spec/sentence_analyzer_spec.ts +293 -0
  100. package/ts_spec/sentence_tokenizer_parser_spec.ts +200 -0
  101. package/ts_spec/sentence_tokenizer_spec.ts +189 -0
  102. package/ts_spec/shortest_path_tree_spec.ts +141 -0
  103. package/ts_spec/soundex_spec.ts +162 -0
  104. package/ts_spec/spellcheck_spec.ts +112 -0
  105. package/ts_spec/stemmer_id_spec.ts +374 -0
  106. package/ts_spec/stemmer_ja_spec.ts +69 -0
  107. package/ts_spec/stemmer_token_spec.ts +163 -0
  108. package/ts_spec/tfidf_spec.ts +275 -0
  109. package/ts_spec/tokenizer_case_spec.ts +242 -0
  110. package/ts_spec/tokenizer_ja_spec.ts +63 -0
  111. package/ts_spec/transliterator_ja_spec.ts +105 -0
  112. package/ts_spec/treebank_word_tokenizer_spec.ts +38 -0
  113. package/ts_spec/trie_spec.ts +197 -0
  114. package/tsconfig.json +5 -3
  115. package/example.txt +0 -1
  116. package/f2f5d4bc-daa0-4392-8981-9614cd0c802e.json +0 -1
@@ -24,477 +24,475 @@ THE SOFTWARE.
24
24
 
25
25
  const Phonetic = require('./phonetic')
26
26
 
27
- const DoubleMetaphone = new Phonetic()
28
- module.exports = DoubleMetaphone
27
+ class DoubleMetaphone extends Phonetic {
28
+ isVowel (c) {
29
+ return c && c.match(/[aeiouy]/i)
30
+ }
29
31
 
30
- function isVowel (c) {
31
- return c && c.match(/[aeiouy]/i)
32
- }
32
+ process (token, maxLength) {
33
+ // For the functions that live here in the process method
34
+ const self = this
33
35
 
34
- function truncate (string, length) {
35
- if (string.length >= length) { string = string.substring(0, length) }
36
+ token = token.toUpperCase()
37
+ let primary = ''; let secondary = ''
38
+ let pos = 0
39
+ maxLength = maxLength || 32
36
40
 
37
- return string
38
- }
41
+ const san = (token.substring(0, 3) === 'SAN')
42
+ const startsWithVowel = this.isVowel(token[0])
43
+ const slavoGermanic = token.match(/(W|K|CZ|WITZ)/)
39
44
 
40
- function process (token, maxLength) {
41
- token = token.toUpperCase()
42
- let primary = ''; let secondary = ''
43
- let pos = 0
44
- maxLength = maxLength || 32
45
+ if (subMatch(0, 2, ['GN', 'KN', 'PN', 'WR', 'PS'])) {
46
+ pos++
47
+ }
45
48
 
46
- function subMatch (startOffset, stopOffset, terms) {
47
- return subMatchAbsolute(pos + startOffset, pos + stopOffset, terms)
48
- }
49
+ while (pos < token.length) {
50
+ switch (token[pos]) {
51
+ case 'A': case 'E': case 'I': case 'O': case 'U': case 'Y':
52
+ case 'Ê': case 'É': case 'À':
53
+ if (pos === 0) { add('A') }
54
+ break
55
+ case 'B':
56
+ addCompressedDouble('B', 'P')
57
+ break
58
+ case 'C':
59
+ handleC()
60
+ break
61
+ case 'Ç':
62
+ add('S')
63
+ break
64
+ case 'D':
65
+ handleD()
66
+ break
67
+ case 'F': case 'K': case 'N':
68
+ addCompressedDouble(token[pos])
69
+ break
70
+ case 'G':
71
+ handleG()
72
+ break
73
+ case 'H':
74
+ handleH()
75
+ break
76
+ case 'J':
77
+ handleJ()
78
+ break
79
+ case 'L':
80
+ handleL()
81
+ break
82
+ case 'M':
83
+ handleM()
84
+ break
85
+ case 'Ñ':
86
+ add('N')
87
+ break
88
+ case 'P':
89
+ handleP()
90
+ break
91
+ case 'Q':
92
+ addCompressedDouble('Q', 'K')
93
+ break
94
+ case 'R':
95
+ handleR()
96
+ break
97
+ case 'S':
98
+ handleS()
99
+ break
100
+ case 'T':
101
+ handleT()
102
+ break
103
+ case 'V':
104
+ addCompressedDouble('V', 'F')
105
+ break
106
+ case 'W':
107
+ handleW()
108
+ break
109
+ case 'X':
110
+ handleX()
111
+ break
112
+ case 'Z':
113
+ handleZ()
114
+ break
115
+ }
49
116
 
50
- function subMatchAbsolute (startOffset, stopOffset, terms) {
51
- return terms.indexOf(token.substring(startOffset, stopOffset)) > -1
52
- }
117
+ if (primary.length >= maxLength && secondary.length >= maxLength) {
118
+ break
119
+ }
53
120
 
54
- function addSecondary (primaryAppendage, secondaryAppendage) {
55
- primary += primaryAppendage
56
- secondary += secondaryAppendage
57
- }
121
+ pos++
122
+ }
58
123
 
59
- function add (primaryAppendage) {
60
- addSecondary(primaryAppendage, primaryAppendage)
61
- }
124
+ return [truncate(primary, maxLength), truncate(secondary, maxLength)]
62
125
 
63
- function addCompressedDouble (c, encoded) {
64
- if (token[pos + 1] === c) {
65
- pos++
126
+ function subMatch (startOffset, stopOffset, terms) {
127
+ return subMatchAbsolute(pos + startOffset, pos + stopOffset, terms)
66
128
  }
67
- add(encoded || c)
68
- }
69
129
 
70
- function handleC () {
71
- if ((pos >= 1 && !isVowel(token[pos - 2]) &&
72
- token[pos - 1] === 'A' && token[pos + 1] === 'H' &&
73
- token[pos + 2] !== 'I') ||
74
- subMatch(-2, 4, ['BACHER', 'MACHER'])) {
75
- add('K')
76
- pos++
77
- } else if (pos === 0 && token.substring(1, 6) === 'EASAR') {
78
- add('S')
79
- add('S')
80
- add('R')
81
- pos += 6
82
- } else if (token.substring(pos + 1, pos + 4) === 'HIA') {
83
- add('K')
84
- pos++
85
- } else if (token[pos + 1] === 'H') {
86
- if (pos > 0 && token.substring(pos + 2, pos + 4) === 'AE') {
87
- addSecondary('K', 'X')
130
+ function subMatchAbsolute (startOffset, stopOffset, terms) {
131
+ return terms.indexOf(token.substring(startOffset, stopOffset)) > -1
132
+ }
133
+
134
+ function truncate (string, length) {
135
+ if (string.length >= length) { string = string.substring(0, length) }
136
+
137
+ return string
138
+ }
139
+
140
+ function addSecondary (primaryAppendage, secondaryAppendage) {
141
+ primary += primaryAppendage
142
+ secondary += secondaryAppendage
143
+ }
144
+
145
+ function add (primaryAppendage) {
146
+ addSecondary(primaryAppendage, primaryAppendage)
147
+ }
148
+
149
+ function addCompressedDouble (c, encoded) {
150
+ if (token[pos + 1] === c) {
88
151
  pos++
89
- } else if (pos === 0 &&
90
- (subMatch(1, 6, ['HARAC', 'HARIS']) ||
91
- subMatch(1, 4, ['HOR', 'HUM', 'HIA', 'HEM'])) &&
92
- token.substring(pos + 1, pos + 5) !== 'HORE') {
152
+ }
153
+ add(encoded || c)
154
+ }
155
+
156
+ function handleC () {
157
+ if ((pos >= 1 && !self.isVowel(token[pos - 2]) &&
158
+ token[pos - 1] === 'A' && token[pos + 1] === 'H' &&
159
+ token[pos + 2] !== 'I') ||
160
+ subMatch(-2, 4, ['BACHER', 'MACHER'])) {
93
161
  add('K')
94
162
  pos++
95
- } else {
96
- if ((subMatchAbsolute(0, 3, ['VAN', 'VON']) || token.substring(0, 3) === 'SCH') ||
97
- subMatch(-2, 4, ['ORCHES', 'ARCHIT', 'ORCHID']) ||
98
- subMatch(2, 3, ['T', 'S']) ||
99
- ((subMatch(-1, 0, ['A', 'O', 'U', 'E']) || pos === 0) &&
100
- subMatch(2, 3, ['B', 'F', 'H', 'L', 'M', 'N', 'R', 'V', 'W']))) {
163
+ } else if (pos === 0 && token.substring(1, 6) === 'EASAR') {
164
+ add('S')
165
+ add('S')
166
+ add('R')
167
+ pos += 6
168
+ } else if (token.substring(pos + 1, pos + 4) === 'HIA') {
169
+ add('K')
170
+ pos++
171
+ } else if (token[pos + 1] === 'H') {
172
+ if (pos > 0 && token.substring(pos + 2, pos + 4) === 'AE') {
173
+ addSecondary('K', 'X')
174
+ pos++
175
+ } else if (pos === 0 &&
176
+ (subMatch(1, 6, ['HARAC', 'HARIS']) ||
177
+ subMatch(1, 4, ['HOR', 'HUM', 'HIA', 'HEM'])) &&
178
+ token.substring(pos + 1, pos + 5) !== 'HORE') {
101
179
  add('K')
102
- } else if (pos > 0) {
103
- if (token.substring(0, 2) === 'MC') {
180
+ pos++
181
+ } else {
182
+ if ((subMatchAbsolute(0, 3, ['VAN', 'VON']) || token.substring(0, 3) === 'SCH') ||
183
+ subMatch(-2, 4, ['ORCHES', 'ARCHIT', 'ORCHID']) ||
184
+ subMatch(2, 3, ['T', 'S']) ||
185
+ ((subMatch(-1, 0, ['A', 'O', 'U', 'E']) || pos === 0) &&
186
+ subMatch(2, 3, ['B', 'F', 'H', 'L', 'M', 'N', 'R', 'V', 'W']))) {
104
187
  add('K')
188
+ } else if (pos > 0) {
189
+ if (token.substring(0, 2) === 'MC') {
190
+ add('K')
191
+ } else {
192
+ addSecondary('X', 'K')
193
+ }
105
194
  } else {
106
- addSecondary('X', 'K')
195
+ add('X')
107
196
  }
108
- } else {
109
- add('X')
197
+ pos++
110
198
  }
111
-
199
+ } else if (token.substring(pos, pos + 2) === 'CZ' &&
200
+ token.substring(pos - 2, pos + 1) !== 'WICZ') {
201
+ addSecondary('S', 'X')
112
202
  pos++
113
- }
114
- } else if (token.substring(pos, pos + 2) === 'CZ' &&
115
- token.substring(pos - 2, pos + 1) !== 'WICZ') {
116
- addSecondary('S', 'X')
117
- pos++
118
- } else if (token.substring(pos, pos + 3) === 'CIA') {
119
- add('X')
120
- pos += 2
121
- } else if (token[pos + 1] === 'C' && pos !== 1 && token[0] !== 'M') {
122
- if (['I', 'E', 'H'].indexOf(token[pos + 2]) > -1 &&
123
- token.substring(pos + 2, pos + 4) !== 'HU') {
124
- if ((pos === 1 && token[pos - 1] === 'A') ||
125
- subMatch(-1, 4, ['UCCEE', 'UCCES'])) {
126
- add('KS')
203
+ } else if (token.substring(pos, pos + 3) === 'CIA') {
204
+ add('X')
205
+ pos += 2
206
+ } else if (token[pos + 1] === 'C' && pos !== 1 && token[0] !== 'M') {
207
+ if (['I', 'E', 'H'].indexOf(token[pos + 2]) > -1 &&
208
+ token.substring(pos + 2, pos + 4) !== 'HU') {
209
+ if ((pos === 1 && token[pos - 1] === 'A') ||
210
+ subMatch(-1, 4, ['UCCEE', 'UCCES'])) {
211
+ add('KS')
212
+ } else {
213
+ add('X')
214
+ }
215
+ pos += 2
127
216
  } else {
128
- add('X')
217
+ add('K')
218
+ pos++
129
219
  }
130
-
131
- pos += 2
132
- } else {
220
+ } else if (['K', 'G', 'Q'].indexOf(token[pos + 1]) > -1) {
133
221
  add('K')
134
222
  pos++
135
- }
136
- } else if (['K', 'G', 'Q'].indexOf(token[pos + 1]) > -1) {
137
- add('K')
138
- pos++
139
- } else if (['E', 'I', 'Y'].indexOf(token[pos + 1]) > -1) {
140
- if (subMatch(1, 3, ['IA', 'IE', 'IO'])) {
141
- addSecondary('S', 'X')
142
- } else {
143
- add('S')
144
- }
145
- pos++
146
- } else {
147
- add('K')
148
- if (token[pos + 1] === ' ' && ['C', 'Q', 'G'].indexOf(token[pos + 2])) {
149
- pos += 2
150
- } else if (['C', 'K', 'Q'].indexOf(token[pos + 1]) > -1 &&
151
- !subMatch(1, 3, ['CE', 'CI'])) {
223
+ } else if (['E', 'I', 'Y'].indexOf(token[pos + 1]) > -1) {
224
+ if (subMatch(1, 3, ['IA', 'IE', 'IO'])) {
225
+ addSecondary('S', 'X')
226
+ } else {
227
+ add('S')
228
+ }
152
229
  pos++
153
- }
154
- }
155
- }
156
-
157
- function handleD () {
158
- if (token[pos + 1] === 'G') {
159
- if (['I', 'E', 'Y'].indexOf(token[pos + 2]) > -1) {
160
- add('J')
161
- pos += 2
162
230
  } else {
163
- add('TK')
164
- pos++
231
+ add('K')
232
+ if (token[pos + 1] === ' ' && ['C', 'Q', 'G'].indexOf(token[pos + 2])) {
233
+ pos += 2
234
+ } else if (['C', 'K', 'Q'].indexOf(token[pos + 1]) > -1 &&
235
+ !subMatch(1, 3, ['CE', 'CI'])) {
236
+ pos++
237
+ }
165
238
  }
166
- } else if (token[pos + 1] === 'T') {
167
- add('T')
168
- pos++
169
- } else { addCompressedDouble('D', 'T') }
170
- }
239
+ }
171
240
 
172
- function handleG () {
173
- if (token[pos + 1] === 'H') {
174
- if (pos > 0 && !isVowel(token[pos - 1])) {
175
- add('K')
176
- pos++
177
- } else if (pos === 0) {
178
- if (token[pos + 2] === 'I') {
241
+ function handleD () {
242
+ if (token[pos + 1] === 'G') {
243
+ if (['I', 'E', 'Y'].indexOf(token[pos + 2]) > -1) {
179
244
  add('J')
245
+ pos += 2
180
246
  } else {
181
- add('K')
247
+ add('TK')
248
+ pos++
182
249
  }
250
+ } else if (token[pos + 1] === 'T') {
251
+ add('T')
183
252
  pos++
184
- } else if (pos > 1 &&
185
- (['B', 'H', 'D'].indexOf(token[pos - 2]) > -1 ||
186
- ['B', 'H', 'D'].indexOf(token[pos - 3]) > -1 ||
187
- ['B', 'H'].indexOf(token[pos - 4]) > -1)) {
188
- pos++
189
- } else {
190
- if (pos > 2 &&
191
- token[pos - 1] === 'U' &&
192
- ['C', 'G', 'L', 'R', 'T'].indexOf(token[pos - 3]) > -1) {
193
- add('F')
194
- } else if (token[pos - 1] !== 'I') {
253
+ } else { addCompressedDouble('D', 'T') }
254
+ }
255
+
256
+ function handleG () {
257
+ if (token[pos + 1] === 'H') {
258
+ if (pos > 0 && !self.isVowel(token[pos - 1])) {
195
259
  add('K')
196
- }
260
+ pos++
261
+ } else if (pos === 0) {
262
+ if (token[pos + 2] === 'I') {
263
+ add('J')
264
+ } else {
265
+ add('K')
266
+ }
267
+ pos++
268
+ } else if (pos > 1 &&
269
+ (['B', 'H', 'D'].indexOf(token[pos - 2]) > -1 ||
270
+ ['B', 'H', 'D'].indexOf(token[pos - 3]) > -1 ||
271
+ ['B', 'H'].indexOf(token[pos - 4]) > -1)) {
272
+ pos++
273
+ } else {
274
+ if (pos > 2 &&
275
+ token[pos - 1] === 'U' &&
276
+ ['C', 'G', 'L', 'R', 'T'].indexOf(token[pos - 3]) > -1) {
277
+ add('F')
278
+ } else if (token[pos - 1] !== 'I') {
279
+ add('K')
280
+ }
197
281
 
282
+ pos++
283
+ }
284
+ } else if (token[pos + 1] === 'N') {
285
+ if (pos === 1 && startsWithVowel && !slavoGermanic) {
286
+ addSecondary('KN', 'N')
287
+ } else {
288
+ if (token.substring(pos + 2, pos + 4) !== 'EY' &&
289
+ (token[pos + 1] !== 'Y' &&
290
+ !slavoGermanic)) {
291
+ addSecondary('N', 'KN')
292
+ } else { add('KN') }
293
+ }
198
294
  pos++
199
- }
200
- } else if (token[pos + 1] === 'N') {
201
- if (pos === 1 && startsWithVowel && !slavoGermanic) {
202
- addSecondary('KN', 'N')
295
+ } else if (token.substring(pos + 1, pos + 3) === 'LI' && !slavoGermanic) {
296
+ addSecondary('KL', 'L')
297
+ pos++
298
+ } else if (pos === 0 && (token[pos + 1] === 'Y' ||
299
+ subMatch(1, 3, ['ES', 'EP', 'EB', 'EL', 'EY', 'IB', 'IL', 'IN', 'IE', 'EI', 'ER']))) {
300
+ addSecondary('K', 'J')
203
301
  } else {
204
- if (token.substring(pos + 2, pos + 4) !== 'EY' &&
205
- (token[pos + 1] !== 'Y' &&
206
- !slavoGermanic)) {
207
- addSecondary('N', 'KN')
208
- } else { add('KN') }
302
+ addCompressedDouble('G', 'K')
209
303
  }
210
- pos++
211
- } else if (token.substring(pos + 1, pos + 3) === 'LI' && !slavoGermanic) {
212
- addSecondary('KL', 'L')
213
- pos++
214
- } else if (pos === 0 && (token[pos + 1] === 'Y' ||
215
- subMatch(1, 3, ['ES', 'EP', 'EB', 'EL', 'EY', 'IB', 'IL', 'IN', 'IE', 'EI', 'ER']))) {
216
- addSecondary('K', 'J')
217
- } else {
218
- addCompressedDouble('G', 'K')
219
- }
220
- }
221
-
222
- function handleH () {
223
- // keep if starts a word or is surrounded by vowels
224
- if ((pos === 0 || isVowel(token[pos - 1])) && isVowel(token[pos + 1])) {
225
- add('H')
226
- pos++
227
304
  }
228
- }
229
305
 
230
- function handleJ () {
231
- const jose = (token.substring(pos + 1, pos + 4) === 'OSE')
232
-
233
- if (san || jose) {
234
- if ((pos === 0 && token[pos + 4] === ' ') ||
235
- san) {
306
+ function handleH () {
307
+ // keep if starts a word or is surrounded by vowels
308
+ if ((pos === 0 || self.isVowel(token[pos - 1])) && self.isVowel(token[pos + 1])) {
236
309
  add('H')
237
- } else { add('J', 'H') }
238
- } else {
239
- if (pos === 0/* && !jose */) {
240
- addSecondary('J', 'A')
241
- } else if (isVowel(token[pos - 1]) && !slavoGermanic &&
242
- (token[pos + 1] === 'A' || token[pos + 1] === 'O')) {
243
- addSecondary('J', 'H')
244
- } else if (pos === token.length - 1) {
245
- addSecondary('J', ' ')
246
- } else { addCompressedDouble('J') }
247
- }
248
- }
249
-
250
- function handleL () {
251
- if (token[pos + 1] === 'L') {
252
- if (pos === token.length - 3 && (
253
- subMatch(-1, 3, ['ILLO', 'ILLA', 'ALLE']) || (
254
- token.substring(pos - 1, pos + 3) === 'ALLE' &&
255
- (subMatch(-2, -1, ['AS', 'OS']) > -1 ||
256
- ['A', 'O'].indexOf(token[token.length - 1]) > -1)))) {
257
- addSecondary('L', '')
258
310
  pos++
259
- return
260
311
  }
261
- pos++
262
312
  }
263
- add('L')
264
- }
265
313
 
266
- function handleM () {
267
- addCompressedDouble('M')
268
- if (token[pos - 1] === 'U' && token[pos + 1] === 'B' &&
269
- ((pos === token.length - 2 || token.substring(pos + 2, pos + 4) === 'ER'))) { pos++ }
270
- }
314
+ function handleJ () {
315
+ const jose = (token.substring(pos + 1, pos + 4) === 'OSE')
271
316
 
272
- function handleP () {
273
- if (token[pos + 1] === 'H') {
274
- add('F')
275
- pos++
276
- } else {
277
- addCompressedDouble('P')
278
- if (token[pos + 1] === 'B') {
317
+ if (san || jose) {
318
+ if ((pos === 0 && token[pos + 4] === ' ') ||
319
+ san) {
320
+ add('H')
321
+ } else { add('J', 'H') }
322
+ } else {
323
+ if (pos === 0/* && !jose */) {
324
+ addSecondary('J', 'A')
325
+ } else if (self.isVowel(token[pos - 1]) && !slavoGermanic &&
326
+ (token[pos + 1] === 'A' || token[pos + 1] === 'O')) {
327
+ addSecondary('J', 'H')
328
+ } else if (pos === token.length - 1) {
329
+ addSecondary('J', ' ')
330
+ } else { addCompressedDouble('J') }
331
+ }
332
+ }
333
+
334
+ function handleL () {
335
+ if (token[pos + 1] === 'L') {
336
+ if (pos === token.length - 3 && (
337
+ subMatch(-1, 3, ['ILLO', 'ILLA', 'ALLE']) || (
338
+ token.substring(pos - 1, pos + 3) === 'ALLE' &&
339
+ (subMatch(-2, -1, ['AS', 'OS']) > -1 ||
340
+ ['A', 'O'].indexOf(token[token.length - 1]) > -1)))) {
341
+ addSecondary('L', '')
342
+ pos++
343
+ return
344
+ }
279
345
  pos++
280
346
  }
347
+ add('L')
281
348
  }
282
- }
283
349
 
284
- function handleR () {
285
- if (pos === token.length - 1 && !slavoGermanic &&
286
- token.substring(pos - 2, pos) === 'IE' &&
287
- !subMatch(-4, -3, ['ME', 'MA'])) {
288
- addSecondary('', 'R')
289
- } else {
290
- addCompressedDouble('R')
350
+ function handleM () {
351
+ addCompressedDouble('M')
352
+ if (token[pos - 1] === 'U' && token[pos + 1] === 'B' &&
353
+ ((pos === token.length - 2 || token.substring(pos + 2, pos + 4) === 'ER'))) { pos++ }
291
354
  }
292
- }
293
355
 
294
- function handleS () {
295
- if (pos === 0 && token.substring(0, 5) === 'SUGAR') {
296
- addSecondary('X', 'S')
297
- } else if (token[pos + 1] === 'H') {
298
- if (subMatch(2, 5, ['EIM', 'OEK', 'OLM', 'OLZ'])) {
299
- add('S')
356
+ function handleP () {
357
+ if (token[pos + 1] === 'H') {
358
+ add('F')
359
+ pos++
300
360
  } else {
301
- add('X')
361
+ addCompressedDouble('P')
362
+ if (token[pos + 1] === 'B') {
363
+ pos++
364
+ }
302
365
  }
303
- pos++
304
- } else if (subMatch(1, 3, ['IO', 'IA'])) {
305
- if (slavoGermanic) {
306
- add('S')
366
+ }
367
+
368
+ function handleR () {
369
+ if (pos === token.length - 1 && !slavoGermanic &&
370
+ token.substring(pos - 2, pos) === 'IE' &&
371
+ !subMatch(-4, -3, ['ME', 'MA'])) {
372
+ addSecondary('', 'R')
307
373
  } else {
308
- addSecondary('S', 'X')
374
+ addCompressedDouble('R')
309
375
  }
310
- pos++
311
- } else if ((pos === 0 && ['M', 'N', 'L', 'W'].indexOf(token[pos + 1]) > -1) ||
312
- token[pos + 1] === 'Z') {
313
- addSecondary('S', 'X')
314
- if (token[pos + 1] === 'Z') { pos++ }
315
- } else if (token.substring(pos, pos + 2) === 'SC') {
316
- if (token[pos + 2] === 'H') {
317
- if (subMatch(3, 5, ['ER', 'EN'])) {
318
- addSecondary('X', 'SK')
319
- } else if (subMatch(3, 5, ['OO', 'UY', 'ED', 'EM'])) {
320
- add('SK')
321
- } else if (pos === 0 && !isVowel(token[3]) && token[3] !== 'W') {
322
- addSecondary('X', 'S')
376
+ }
377
+
378
+ function handleS () {
379
+ if (pos === 0 && token.substring(0, 5) === 'SUGAR') {
380
+ addSecondary('X', 'S')
381
+ } else if (token[pos + 1] === 'H') {
382
+ if (subMatch(2, 5, ['EIM', 'OEK', 'OLM', 'OLZ'])) {
383
+ add('S')
323
384
  } else {
324
385
  add('X')
325
386
  }
326
- } else if (['I', 'E', 'Y'].indexOf(token[pos + 2]) > -1) {
327
- add('S')
328
- } else {
329
- add('SK')
330
- }
387
+ pos++
388
+ } else if (subMatch(1, 3, ['IO', 'IA'])) {
389
+ if (slavoGermanic) {
390
+ add('S')
391
+ } else {
392
+ addSecondary('S', 'X')
393
+ }
394
+ pos++
395
+ } else if ((pos === 0 && ['M', 'N', 'L', 'W'].indexOf(token[pos + 1]) > -1) ||
396
+ token[pos + 1] === 'Z') {
397
+ addSecondary('S', 'X')
398
+ if (token[pos + 1] === 'Z') { pos++ }
399
+ } else if (token.substring(pos, pos + 2) === 'SC') {
400
+ if (token[pos + 2] === 'H') {
401
+ if (subMatch(3, 5, ['ER', 'EN'])) {
402
+ addSecondary('X', 'SK')
403
+ } else if (subMatch(3, 5, ['OO', 'UY', 'ED', 'EM'])) {
404
+ add('SK')
405
+ } else if (pos === 0 && !self.isVowel(token[3]) && token[3] !== 'W') {
406
+ addSecondary('X', 'S')
407
+ } else {
408
+ add('X')
409
+ }
410
+ } else if (['I', 'E', 'Y'].indexOf(token[pos + 2]) > -1) {
411
+ add('S')
412
+ } else {
413
+ add('SK')
414
+ }
331
415
 
332
- pos += 2
333
- } else if (pos === token.length - 1 &&
334
- subMatch(-2, 0, ['AI', 'OI'])) {
335
- addSecondary('', 'S')
336
- } else if (token[pos + 1] !== 'L' && (
337
- token[pos - 1] !== 'A' && token[pos - 1] !== 'I')) {
338
- addCompressedDouble('S')
339
- if (token[pos + 1] === 'Z') { pos++ }
416
+ pos += 2
417
+ } else if (pos === token.length - 1 &&
418
+ subMatch(-2, 0, ['AI', 'OI'])) {
419
+ addSecondary('', 'S')
420
+ } else if (token[pos + 1] !== 'L' && (
421
+ token[pos - 1] !== 'A' && token[pos - 1] !== 'I')) {
422
+ addCompressedDouble('S')
423
+ if (token[pos + 1] === 'Z') { pos++ }
424
+ }
340
425
  }
341
- }
342
426
 
343
- function handleT () {
344
- if (token.substring(pos + 1, pos + 4) === 'ION') {
345
- add('XN')
346
- pos += 3
347
- } else if (subMatch(1, 3, ['IA', 'CH'])) {
348
- add('X')
349
- pos += 2
350
- } else if (token[pos + 1] === 'H' ||
351
- token.substring(1, 2) === 'TH') {
352
- if (subMatch(2, 4, ['OM', 'AM']) ||
353
- ['VAN ', 'VON '].indexOf(token.substring(0, 4)) > -1 ||
354
- token.substring(0, 3) === 'SCH') {
355
- add('T')
356
- } else { addSecondary('0', 'T') }
357
- pos++
358
- } else {
359
- addCompressedDouble('T')
427
+ function handleT () {
428
+ if (token.substring(pos + 1, pos + 4) === 'ION') {
429
+ add('XN')
430
+ pos += 3
431
+ } else if (subMatch(1, 3, ['IA', 'CH'])) {
432
+ add('X')
433
+ pos += 2
434
+ } else if (token[pos + 1] === 'H' ||
435
+ token.substring(1, 2) === 'TH') {
436
+ if (subMatch(2, 4, ['OM', 'AM']) ||
437
+ ['VAN ', 'VON '].indexOf(token.substring(0, 4)) > -1 ||
438
+ token.substring(0, 3) === 'SCH') {
439
+ add('T')
440
+ } else { addSecondary('0', 'T') }
441
+ pos++
442
+ } else {
443
+ addCompressedDouble('T')
360
444
 
361
- if (token[pos + 1] === 'D') { pos++ }
445
+ if (token[pos + 1] === 'D') { pos++ }
446
+ }
362
447
  }
363
- }
364
448
 
365
- function handleX () {
366
- if (pos === 0) {
367
- add('S')
368
- } else if (!(pos === token.length - 1 &&
369
- (['IAU', 'EAU', 'IEU'].indexOf(token.substring(pos - 3, pos)) > -1 ||
370
- ['AU', 'OU'].indexOf(token.substring(pos - 2, pos)) > -1))) {
371
- add('KS')
449
+ function handleX () {
450
+ if (pos === 0) {
451
+ add('S')
452
+ } else if (!(pos === token.length - 1 &&
453
+ (['IAU', 'EAU', 'IEU'].indexOf(token.substring(pos - 3, pos)) > -1 ||
454
+ ['AU', 'OU'].indexOf(token.substring(pos - 2, pos)) > -1))) {
455
+ add('KS')
456
+ }
372
457
  }
373
- }
374
458
 
375
- function handleW () {
376
- if (pos === 0) {
377
- if (token[1] === 'H') {
378
- add('A')
379
- } else if (isVowel(token[1])) {
380
- addSecondary('A', 'F')
459
+ function handleW () {
460
+ if (pos === 0) {
461
+ if (token[1] === 'H') {
462
+ add('A')
463
+ } else if (self.isVowel(token[1])) {
464
+ addSecondary('A', 'F')
465
+ }
466
+ } else if (subMatch(-1, 4, ['EWSKI', 'EWSKY', 'OWSKI', 'OWSKY']) ||
467
+ token.substring(0, 3) === 'SCH' ||
468
+ (pos === token.length - 1 && self.isVowel(token[pos - 1]))) {
469
+ addSecondary('', 'F')
470
+ pos++
471
+ } else if (['ICZ', 'ITZ'].indexOf(token.substring(pos + 1, pos + 4)) > -1) {
472
+ addSecondary('TS', 'FX')
473
+ pos += 3
381
474
  }
382
- } else if (subMatch(-1, 4, ['EWSKI', 'EWSKY', 'OWSKI', 'OWSKY']) ||
383
- token.substring(0, 3) === 'SCH' ||
384
- (pos === token.length - 1 && isVowel(token[pos - 1]))) {
385
- addSecondary('', 'F')
386
- pos++
387
- } else if (['ICZ', 'ITZ'].indexOf(token.substring(pos + 1, pos + 4)) > -1) {
388
- addSecondary('TS', 'FX')
389
- pos += 3
390
475
  }
391
- }
392
476
 
393
- function handleZ () {
394
- if (token[pos + 1] === 'H') {
395
- add('J')
396
- pos++
397
- } else if (subMatch(1, 3, ['ZO', 'ZI', 'ZA']) ||
398
- (slavoGermanic && pos > 0 && token[pos - 1] !== 'T')) {
399
- addSecondary('S', 'TS')
400
- pos++
401
- } else { addCompressedDouble('Z', 'S') }
402
- }
403
-
404
- const san = (token.substring(0, 3) === 'SAN')
405
- const startsWithVowel = isVowel(token[0])
406
- const slavoGermanic = token.match(/(W|K|CZ|WITZ)/)
407
-
408
- if (subMatch(0, 2, ['GN', 'KN', 'PN', 'WR', 'PS'])) {
409
- pos++
410
- }
411
-
412
- while (pos < token.length) {
413
- switch (token[pos]) {
414
- case 'A': case 'E': case 'I': case 'O': case 'U': case 'Y':
415
- case 'Ê': case 'É': case 'À':
416
- if (pos === 0) { add('A') }
417
- break
418
- case 'B':
419
- addCompressedDouble('B', 'P')
420
- break
421
- case 'C':
422
- handleC()
423
- break
424
- case 'Ç':
425
- add('S')
426
- break
427
- case 'D':
428
- handleD()
429
- break
430
- case 'F': case 'K': case 'N':
431
- addCompressedDouble(token[pos])
432
- break
433
- case 'G':
434
- handleG()
435
- break
436
- case 'H':
437
- handleH()
438
- break
439
- case 'J':
440
- handleJ()
441
- break
442
- case 'L':
443
- handleL()
444
- break
445
- case 'M':
446
- handleM()
447
- break
448
- case 'Ñ':
449
- add('N')
450
- break
451
- case 'P':
452
- handleP()
453
- break
454
- case 'Q':
455
- addCompressedDouble('Q', 'K')
456
- break
457
- case 'R':
458
- handleR()
459
- break
460
- case 'S':
461
- handleS()
462
- break
463
- case 'T':
464
- handleT()
465
- break
466
- case 'V':
467
- addCompressedDouble('V', 'F')
468
- break
469
- case 'W':
470
- handleW()
471
- break
472
- case 'X':
473
- handleX()
474
- break
475
- case 'Z':
476
- handleZ()
477
- break
477
+ function handleZ () {
478
+ if (token[pos + 1] === 'H') {
479
+ add('J')
480
+ pos++
481
+ } else if (subMatch(1, 3, ['ZO', 'ZI', 'ZA']) ||
482
+ (slavoGermanic && pos > 0 && token[pos - 1] !== 'T')) {
483
+ addSecondary('S', 'TS')
484
+ pos++
485
+ } else { addCompressedDouble('Z', 'S') }
478
486
  }
487
+ }
479
488
 
480
- if (primary.length >= maxLength && secondary.length >= maxLength) {
481
- break
482
- }
489
+ compare (stringA, stringB) {
490
+ const encodingsA = this.process(stringA)
491
+ const encodingsB = this.process(stringB)
483
492
 
484
- pos++
493
+ return encodingsA[0] === encodingsB[0] ||
494
+ encodingsA[1] === encodingsB[1]
485
495
  }
486
-
487
- return [truncate(primary, maxLength), truncate(secondary, maxLength)]
488
496
  }
489
497
 
490
- function compare (stringA, stringB) {
491
- const encodingsA = process(stringA)
492
- const encodingsB = process(stringB)
493
-
494
- return encodingsA[0] === encodingsB[0] ||
495
- encodingsA[1] === encodingsB[1]
496
- };
497
-
498
- DoubleMetaphone.compare = compare
499
- DoubleMetaphone.process = process
500
- DoubleMetaphone.isVowel = isVowel
498
+ module.exports = DoubleMetaphone