@empyria/name 0.1.2 → 0.1.4
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.
- package/lib/Name.js +65 -0
- package/package.json +1 -1
- package/test/Name.test.js +23 -0
package/lib/Name.js
CHANGED
|
@@ -92,6 +92,17 @@ export const TITLE_LIST = [
|
|
|
92
92
|
]
|
|
93
93
|
export const TITLES = enumise(TITLE_LIST)
|
|
94
94
|
|
|
95
|
+
// Honorifics as compact keys (transliterated, alphanumerics only, upper-case) — the form
|
|
96
|
+
// `nameFp` compares each token against. `TITLES` (an enumise() object) can't be used directly:
|
|
97
|
+
// it has no `.has`, its keys carry `_` separators, and its values keep original casing.
|
|
98
|
+
const TITLE_KEYS = new Set(
|
|
99
|
+
TITLE_LIST.map((t) =>
|
|
100
|
+
transliterate(t)
|
|
101
|
+
.toUpperCase()
|
|
102
|
+
.replace(/[^A-Z0-9]/g, ''),
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
|
|
95
106
|
export function findTitle(_title) {
|
|
96
107
|
if (!_title) return undefined
|
|
97
108
|
|
|
@@ -99,4 +110,58 @@ export function findTitle(_title) {
|
|
|
99
110
|
return TITLE_LIST.find((t) => transliterate(t).toUpperCase() === title)
|
|
100
111
|
}
|
|
101
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Base fold: transliterate to ASCII, collapse any run of whitespace to a single space, trim.
|
|
115
|
+
* @param {unknown} value
|
|
116
|
+
* @returns {string}
|
|
117
|
+
*/
|
|
118
|
+
export function fold(value) {
|
|
119
|
+
return transliterate(String(value ?? ''))
|
|
120
|
+
.replace(/\s+/g, ' ')
|
|
121
|
+
.trim()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Compact identifier key — transliterated, alphanumerics only, upper-case. Format-agnostic:
|
|
126
|
+
* `idKey('5299 00 T8BM49') === idKey('529900t8bm49')`, `idKey('DE 123.456.789') === 'DE123456789'`.
|
|
127
|
+
* Empty string for empty/nullish input (callers skip those).
|
|
128
|
+
* @param {unknown} value
|
|
129
|
+
* @returns {string}
|
|
130
|
+
*/
|
|
131
|
+
export function idKey(value) {
|
|
132
|
+
return fold(value)
|
|
133
|
+
.toUpperCase()
|
|
134
|
+
.replace(/[^A-Z0-9]/g, '')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Free-text name form for external full-text engines (BAILII, CourtListener, opencaselaw) —
|
|
139
|
+
* transliterated, lower-case, single-spaced, tokens intact so their tokenisers still work.
|
|
140
|
+
* @param {unknown} value
|
|
141
|
+
* @returns {string}
|
|
142
|
+
*/
|
|
143
|
+
export function nameText(value) {
|
|
144
|
+
return fold(value).toLowerCase()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Order-, case-, punctuation-, script-invariant name fingerprint: transliterate, upper-case,
|
|
149
|
+
* split on whitespace, strip non-alphanumerics per token, drop honorifics and 1-char tokens,
|
|
150
|
+
* dedupe, sort. `nameFp('Dr. Steve Charles') === nameFp('charles, steve') === 'CHARLES STEVE'`.
|
|
151
|
+
*
|
|
152
|
+
* Best-effort for organisations — legal-form / "&" vs "and" differences aren't reconciled here
|
|
153
|
+
* (`nameFp('J.P. Morgan & Co') !== nameFp('JPMorgan and Co')`); the `:Ident` exact match and the
|
|
154
|
+
* raw-name text search are the reliable paths for companies.
|
|
155
|
+
* @param {unknown} value
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
export function nameFp(value) {
|
|
159
|
+
const tokens = fold(value)
|
|
160
|
+
.toUpperCase()
|
|
161
|
+
.split(' ')
|
|
162
|
+
.map((token) => token.replace(/[^A-Z0-9]/g, ''))
|
|
163
|
+
.filter((token) => token.length > 1 && !TITLE_KEYS.has(token))
|
|
164
|
+
return [...new Set(tokens)].sort().join(' ')
|
|
165
|
+
}
|
|
166
|
+
|
|
102
167
|
export { transliterate, slugify, TRANS, capitalise, shortify }
|
package/package.json
CHANGED
package/test/Name.test.js
CHANGED
|
@@ -3,6 +3,10 @@ import {
|
|
|
3
3
|
TITLE_LIST,
|
|
4
4
|
TITLES,
|
|
5
5
|
findTitle,
|
|
6
|
+
fold,
|
|
7
|
+
idKey,
|
|
8
|
+
nameFp,
|
|
9
|
+
nameText,
|
|
6
10
|
TRANS,
|
|
7
11
|
capitalise,
|
|
8
12
|
shortify,
|
|
@@ -73,3 +77,22 @@ describe('re-exported string helpers (from @empyria/classification)', () => {
|
|
|
73
77
|
expect(slugify('Jean Pierre')).toBe('jean-pierre')
|
|
74
78
|
})
|
|
75
79
|
})
|
|
80
|
+
|
|
81
|
+
describe('fold / idKey / nameText / nameFp', () => {
|
|
82
|
+
test('idKey is format-agnostic', () => {
|
|
83
|
+
expect(idKey('5299 00 T8BM49')).toBe(idKey('529900t8bm49'))
|
|
84
|
+
expect(idKey('DE 123.456.789')).toBe('DE123456789')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('nameText transliterates + lower-cases, keeps token spacing', () => {
|
|
88
|
+
expect(nameText(' José MÜLLER ')).toBe('jose muller')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('nameFp is order / case / punctuation / honorific / script invariant', () => {
|
|
92
|
+
expect(nameFp('Steve Charles')).toBe(nameFp('Charles Steve'))
|
|
93
|
+
expect(nameFp('Dr. Steve Charles')).toBe(nameFp('charles, steve'))
|
|
94
|
+
expect(nameFp('Professor Ada Lovelace')).toBe(nameFp('lovelace, ada'))
|
|
95
|
+
expect(nameFp('Владимир Путин')).toBe(nameFp('Putin Vladimir'))
|
|
96
|
+
expect(nameFp('Mr.')).toBe('')
|
|
97
|
+
})
|
|
98
|
+
})
|