@natlibfi/marc-record-validators-melinda 10.14.0 → 10.15.0-alpha.1

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.
@@ -0,0 +1,69 @@
1
+ //import createDebugLogger from 'debug';
2
+ import clone from 'clone';
3
+ import {fieldToString, isControlSubfieldCode, nvdebug} from './utils';
4
+
5
+ // Author(s): Nicholas Volk
6
+ export default function () {
7
+
8
+ return {
9
+ description: 'Normalize various dashes to "-"',
10
+ validate, fix
11
+ };
12
+
13
+ function fix(record) {
14
+ nvdebug(`FIX ME`);
15
+ record.fields.forEach(field => {
16
+ fixDashes(field);
17
+ });
18
+
19
+ const res = {message: [], fix: [], valid: true};
20
+ return res;
21
+ }
22
+
23
+ function validate(record) {
24
+ const res = {message: []};
25
+
26
+ nvdebug(`VALIDATE ME`);
27
+ record.fields?.forEach(field => {
28
+ validateField(field, res);
29
+ });
30
+
31
+ res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data
32
+ return res;
33
+ }
34
+
35
+ function validateField(field, res) {
36
+ const orig = fieldToString(field);
37
+ nvdebug(` VALIDATE FIELD '${orig}'`);
38
+
39
+ const normalizedField = fixDashes(clone(field));
40
+ const mod = fieldToString(normalizedField);
41
+ if (orig === mod) { // Fail as the input is "broken"/"crap"/sumthing
42
+ return;
43
+ }
44
+ res.message.push(`'TODO: ${orig}' => '${mod}'`); // eslint-disable-line functional/immutable-data
45
+ return;
46
+ }
47
+ }
48
+
49
+
50
+ function fixDashes(field) {
51
+ if (!field.subfields) {
52
+ return field;
53
+ }
54
+
55
+ nvdebug(`Dashing ${fieldToString(field)}`);
56
+
57
+ field.subfields.forEach(sf => subfieldFixDashes(sf));
58
+
59
+ return field;
60
+
61
+ function subfieldFixDashes(subfield) {
62
+ if (isControlSubfieldCode(subfield.code)) {
63
+ return;
64
+ }
65
+ // Normalize dashes U+2010 ... U+2015 to '-':
66
+ subfield.value = subfield.value.replace(/[\u2010\u2011\u2012\u2013\u2014\u2015]/ug, '-'); // eslint-disable-line functional/immutable-data
67
+ }
68
+
69
+ }
@@ -0,0 +1,52 @@
1
+ import {expect} from 'chai';
2
+ import {MarcRecord} from '@natlibfi/marc-record';
3
+ import validatorFactory from './normalize-dashes';
4
+ import {READERS} from '@natlibfi/fixura';
5
+ import generateTests from '@natlibfi/fixugen';
6
+ import createDebugLogger from 'debug';
7
+
8
+ generateTests({
9
+ callback,
10
+ path: [__dirname, '..', 'test-fixtures', 'normalize-dashes'],
11
+ useMetadataFile: true,
12
+ recurse: false,
13
+ fixura: {
14
+ reader: READERS.JSON
15
+ },
16
+ mocha: {
17
+ before: () => testValidatorFactory()
18
+ }
19
+ });
20
+ const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/normalize-dashes:test');
21
+
22
+ async function testValidatorFactory() {
23
+ const validator = await validatorFactory();
24
+
25
+ expect(validator)
26
+ .to.be.an('object')
27
+ .that.has.any.keys('description', 'validate');
28
+
29
+ expect(validator.description).to.be.a('string');
30
+ expect(validator.validate).to.be.a('function');
31
+ }
32
+
33
+ async function callback({getFixture, enabled = true, fix = false}) {
34
+ if (enabled === false) {
35
+ debug('TEST SKIPPED!');
36
+ return;
37
+ }
38
+
39
+ const validator = await validatorFactory();
40
+ const record = new MarcRecord(getFixture('record.json'));
41
+ const expectedResult = getFixture('expectedResult.json');
42
+ // console.log(expectedResult); // eslint-disable-line
43
+
44
+ if (!fix) {
45
+ const result = await validator.validate(record);
46
+ expect(result).to.eql(expectedResult);
47
+ return;
48
+ }
49
+
50
+ await validator.fix(record);
51
+ expect(record).to.eql(expectedResult);
52
+ }
package/src/sortFields.js CHANGED
@@ -1,22 +1,21 @@
1
1
  // Taken from project marc-record-js, file marcSortFields.js as this contains more and more Melinda-specific rules.
2
2
 
3
3
  import clone from 'clone';
4
- import createDebugLogger from 'debug';
4
+ //import createDebugLogger from 'debug';
5
5
  import {fieldHasSubfield, fieldToString} from './utils';
6
+ import {sortByTag, sortAlphabetically, fieldOrderComparator as globalFieldOrderComparator} from '@natlibfi/marc-record/dist/marcFieldSort';
6
7
  import {isValidSubfield8} from './subfield8Utils';
7
8
  import {isValidSubfield6, subfield6GetOccurrenceNumber} from './subfield6Utils';
8
9
 
9
-
10
- const BIG_BAD_NUMBER = 999.99;
11
-
12
- const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortFields');
10
+ //const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortFields');
13
11
  //const debugData = debug.extend('data');
14
- const debugDev = debug.extend('dev');
12
+ //const debugDev = debug.extend('dev');
15
13
 
14
+ const BIG_BAD_NUMBER = 999999999;
16
15
  export default function () {
17
16
 
18
17
  return {
19
- description: 'Sort fields',
18
+ description: 'Sort fields using both generic and Melinda specific rules',
20
19
  validate, fix
21
20
  };
22
21
 
@@ -84,310 +83,222 @@ export function scoreRelatorTerm(value) { // sortRelatorTerms.js validator shoul
84
83
 
85
84
  export function fieldOrderComparator(fieldA, fieldB) {
86
85
 
86
+ //const sorterFunctions = [sortByTag, sortByIndexTerms, sortAlphabetically, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];
87
+
87
88
  const sorterFunctions = [sortByTag, sortByIndexTerms, sortAlphabetically, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];
89
+ //const sorterFunctions = [sortByIndexTerms, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];
88
90
 
89
- for (const sortFn of sorterFunctions) { // eslint-disable-line functional/no-loop-statements
90
- const result = sortFn(fieldA, fieldB);
91
- debugDev(`${sortFn.name}: '${fieldToString(fieldA)}' vs '${fieldToString(fieldB)}': ${result}`);
92
- if (result !== 0) {
93
- return result;
94
- }
95
- }
91
+ return globalFieldOrderComparator(fieldA, fieldB, sorterFunctions);
92
+ }
96
93
 
97
- return 0;
94
+ function sortByIndexTerms(fieldA, fieldB) { // eslint-disable-line complexity, max-statements
98
95
 
99
- function sortByTag(fieldA, fieldB) {
100
-
101
- function getSortIndex(tag) {
102
- const sortIndex = {
103
- LDR: '000',
104
- STA: '001.1', // STA comes now after 001. However 003+001 form a combo, so I'm not sure...
105
- SID: '999.1',
106
- LOW: '999.2',
107
- CAT: '999.3',
108
- HLI: '999.4'
109
- };
110
-
111
- if (tag in sortIndex) { // <- this allows weights for numeric values as well (not that we use them yet)
112
- return sortIndex[tag];
113
- }
114
- if (isNaN(tag)) {
115
- return '999.9';
116
- }
117
- return tag;
118
- }
96
+ const indexTermFields = ['600', '610', '611', '630', '648', '650', '651', '652', '653', '654', '655', '656', '657', '658', '659', '662'];
119
97
 
120
- const orderA = getSortIndex(fieldA.tag);
121
- const orderB = getSortIndex(fieldB.tag);
98
+ function scoreInd2(val) {
99
+ const ind2Score = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 8, '5': 5, '6': 6, '7': 7};
122
100
 
123
- if (orderA > orderB) {
124
- return 1;
125
- }
126
- if (orderA < orderB) {
127
- return -1;
101
+ if (val in ind2Score) {
102
+ return ind2Score[val];
128
103
  }
104
+ return 9;
105
+ }
129
106
 
107
+ // ATM this is not needed.
108
+ // You may need this, if you change compare function order in sorterFunctions
109
+ // istanbul ignore next
110
+ if (fieldA.tag !== fieldB.tag) {
130
111
  return 0;
131
112
  }
132
113
 
133
- function sortByIndexTerms(fieldA, fieldB) { // eslint-disable-line complexity, max-statements
134
-
135
- const indexTermFields = ['600', '610', '611', '630', '648', '650', '651', '652', '653', '654', '655', '656', '657', '658', '659', '662'];
114
+ if (!indexTermFields.includes(fieldA.tag)) {
115
+ return 0;
116
+ }
136
117
 
137
- function scoreInd2(val) {
138
- const ind2Score = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 8, '5': 5, '6': 6, '7': 7};
118
+ // Puts ind2=4 last
119
+ if (scoreInd2(fieldA.ind2) > scoreInd2(fieldB.ind2)) {
120
+ return 1;
121
+ }
122
+ if (scoreInd2(fieldA.ind2) < scoreInd2(fieldB.ind2)) {
123
+ return -1;
124
+ }
139
125
 
140
- if (val in ind2Score) {
141
- return ind2Score[val];
142
- }
143
- return 9;
144
- }
126
+ function scoreDictionary(dictionary) {
127
+ const dictionarySortIndex = {
128
+ 'yso/fin': 0,
129
+ 'yso/swe': 1,
130
+ 'yso/eng': 2,
131
+ 'slm/fin': 0.1,
132
+ 'slm/swe': 1.1,
133
+ 'kauno/fin': 2.1,
134
+ 'kauno/swe': 2.2,
135
+ 'kaunokki': 4,
136
+ 'bella': 5
137
+ };
145
138
 
146
- // ATM this is not needed.
147
- // You may need this, if you change compare function order in sorterFunctions
148
- // istanbul ignore next
149
- if (fieldA.tag !== fieldB.tag) {
150
- return 0;
139
+ if (dictionary in dictionarySortIndex) {
140
+ return dictionarySortIndex[dictionary];
151
141
  }
142
+ return BIG_BAD_NUMBER;
143
+ }
152
144
 
153
- if (!indexTermFields.includes(fieldA.tag)) {
154
- return 0;
155
- }
145
+ const dictionaryA = selectFirstValue(fieldA, '2');
146
+ const dictionaryB = selectFirstValue(fieldB, '2');
156
147
 
157
- /* Puts ind2=4 last */
158
- if (scoreInd2(fieldA.ind2) > scoreInd2(fieldB.ind2)) {
159
- return 1;
160
- }
161
- if (scoreInd2(fieldA.ind2) < scoreInd2(fieldB.ind2)) {
162
- return -1;
163
- }
148
+ const dictScoreA = scoreDictionary(dictionaryA);
149
+ const dictScoreB = scoreDictionary(dictionaryB);
150
+ // Use priority order for listed dictionaries:
151
+ if (dictScoreA > dictScoreB) {
152
+ return 1;
153
+ }
154
+ if (dictScoreA < dictScoreB) {
155
+ return -1;
156
+ }
157
+ // Unlisted dictionaries: sort $2 value alphabetically:
158
+ //if (dictScoreA === BIG_BAD_NUMBER) {
159
+ if (dictionaryA > dictionaryB) {
160
+ return 1;
161
+ }
162
+ if (dictionaryA < dictionaryB) {
163
+ return -1;
164
+ }
165
+ //}
166
+ return 0;
167
+ }
164
168
 
165
- function scoreDictionary(dictionary) {
166
- const dictionarySortIndex = {
167
- 'yso/fin': 0,
168
- 'yso/swe': 1,
169
- 'yso/eng': 2,
170
- 'slm/fin': 0.1,
171
- 'slm/swe': 1.1,
172
- 'kauno/fin': 2.1,
173
- 'kauno/swe': 2.2,
174
- 'kaunokki': 4,
175
- 'bella': 5
176
- };
177
-
178
- if (dictionary in dictionarySortIndex) {
179
- return dictionarySortIndex[dictionary];
180
- }
181
- return BIG_BAD_NUMBER;
182
- }
183
169
 
184
- const dictionaryA = selectFirstValue(fieldA, '2');
185
- const dictionaryB = selectFirstValue(fieldB, '2');
170
+ function preferKeep(fieldA, fieldB, keepOwner = 'FENNI') {
171
+ const hasKeepA = fieldHasSubfield(fieldA, '9', `${keepOwner}<KEEP>`);
172
+ const hasKeepB = fieldHasSubfield(fieldB, '9', `${keepOwner}<KEEP>`);
186
173
 
187
- const dictScoreA = scoreDictionary(dictionaryA);
188
- const dictScoreB = scoreDictionary(dictionaryB);
189
- // Use priority order for listed dictionaries:
190
- if (dictScoreA > dictScoreB) {
191
- return 1;
192
- }
193
- if (dictScoreA < dictScoreB) {
194
- return -1;
195
- }
196
- // Unlisted dictionaries: sort $2 value alphabetically:
197
- //if (dictScoreA === BIG_BAD_NUMBER) {
198
- if (dictionaryA > dictionaryB) {
199
- return 1;
200
- }
201
- if (dictionaryA < dictionaryB) {
202
- return -1;
203
- }
204
- //}
205
- return 0;
174
+ if (hasKeepA && !hasKeepB) {
175
+ return -1;
176
+ }
177
+ if (!hasKeepA && hasKeepB) {
178
+ return 1;
206
179
  }
207
180
 
208
- function preferKeep(fieldA, fieldB, keepOwner = 'FENNI') {
209
- const hasKeepA = fieldHasSubfield(fieldA, '9', `${keepOwner}<KEEP>`);
210
- const hasKeepB = fieldHasSubfield(fieldB, '9', `${keepOwner}<KEEP>`);
181
+ return 0;
182
+ }
211
183
 
212
- if (hasKeepA && !hasKeepB) {
213
- return -1;
214
- }
215
- if (!hasKeepA && hasKeepB) {
216
- return 1;
217
- }
218
184
 
219
- return 0;
185
+ function preferFenniKeep(fieldA, fieldB) {
186
+ const fenniPreference = preferKeep(fieldA, fieldB, 'FENNI');
187
+ if (fenniPreference !== 0) {
188
+ return fenniPreference;
189
+ }
190
+ const violaPreference = preferKeep(fieldA, fieldB, 'VIOLA');
191
+ if (violaPreference !== 0) {
192
+ return violaPreference;
220
193
  }
194
+ return preferKeep(fieldA, fieldB, 'FIKKA');
195
+ }
221
196
 
222
- function preferFenniKeep(fieldA, fieldB) {
223
- const fenniPreference = preferKeep(fieldA, fieldB, 'FENNI');
224
- if (fenniPreference !== 0) {
225
- return fenniPreference;
226
- }
227
- const violaPreference = preferKeep(fieldA, fieldB, 'VIOLA');
228
- if (violaPreference !== 0) {
229
- return violaPreference;
230
- }
231
- return preferKeep(fieldA, fieldB, 'FIKKA');
197
+ function sortByRelatorTerm(fieldA, fieldB) {
198
+ // Should this be done to 6XX and 8XX fields as well? Does $t affect sorting?
199
+ if (!['700', '710', '711', '730'].includes(fieldA.tag)) {
200
+ return 0;
232
201
  }
233
202
 
234
- function sortByRelatorTerm(fieldA, fieldB) {
235
- // Should this be done to 6XX and 8XX fields as well? Does $t affect sorting?
236
- if (!['700', '710', '711', '730'].includes(fieldA.tag)) {
203
+ function fieldGetMaxRelatorTermScore(field) {
204
+ if (!field.subfields) {
237
205
  return 0;
238
206
  }
207
+ const e = field.subfields.filter(sf => sf.code === 'e');
208
+ const scores = e.map(sf => scoreRelatorTerm(sf.value));
209
+ //debugDev(`RELATOR SCORE FOR '${fieldToString(field)}': ${scores.join(', ')}`);
210
+ return Math.max(...scores);
211
+ }
239
212
 
213
+ const scoreA = fieldGetMaxRelatorTermScore(fieldA);
214
+ const scoreB = fieldGetMaxRelatorTermScore(fieldB);
240
215
 
241
- function fieldGetMaxRelatorTermScore(field) {
242
- if (!field.subfields) {
243
- return 0;
244
- }
245
- const e = field.subfields.filter(sf => sf.code === 'e');
246
- const scores = e.map(sf => scoreRelatorTerm(sf.value));
247
- //debugDev(`RELATOR SCORE FOR '${fieldToString(field)}': ${scores.join(', ')}`);
248
- return Math.max(...scores);
249
- }
216
+ if (scoreA < scoreB) {
217
+ return 1;
218
+ }
219
+ if (scoreA > scoreB) {
220
+ return -1;
221
+ }
222
+ return 0;
223
+ }
250
224
 
251
- const scoreA = fieldGetMaxRelatorTermScore(fieldA);
252
- const scoreB = fieldGetMaxRelatorTermScore(fieldB);
253
225
 
254
- if (scoreA < scoreB) {
255
- return 1;
256
- }
257
- if (scoreA > scoreB) {
258
- return -1;
259
- }
260
- return 0;
226
+ function fieldGetMinLinkAndSequenceNumber(field) {
227
+ if (!field.subfields) {
228
+ return BIG_BAD_NUMBER;
261
229
  }
262
-
263
- function fieldGetMinLinkAndSequenceNumber(field) {
264
- if (!field.subfields) {
265
- return BIG_BAD_NUMBER;
266
- }
267
- const relevantSubfields = field.subfields.filter(sf => isValidSubfield8(sf));
268
- // If val is something like "1.2\x" parseFloat() would give a syntax erro because of hex-like escape sequnce (at least on Chrome). Thus remove tail:
269
- const scores = relevantSubfields.map(sf => parseFloat(sf.value.replace(/\\.*$/u, '')));
270
- if (scores.length === 0) {
271
- return BIG_BAD_NUMBER;
272
- }
273
- return Math.min(...scores);
230
+ const relevantSubfields = field.subfields.filter(sf => isValidSubfield8(sf));
231
+ // If val is something like "1.2\x" parseFloat() would give a syntax erro because of hex-like escape sequnce (at least on Chrome). Thus remove tail:
232
+ const scores = relevantSubfields.map(sf => parseFloat(sf.value.replace(/\\.*$/u, '')));
233
+ if (scores.length === 0) {
234
+ return BIG_BAD_NUMBER;
274
235
  }
236
+ return Math.min(...scores);
237
+ }
275
238
 
276
- function sortByFieldLinkAndSequenceNumber(fieldA, fieldB) { // Sort by subfield $8 that is...
277
- const scoreA = fieldGetMinLinkAndSequenceNumber(fieldA);
278
- const scoreB = fieldGetMinLinkAndSequenceNumber(fieldB);
279
- //debugDev(` sf-8-A-score for '${fieldToString(fieldA)}: ${scoreA}`);
280
- //debugDev(` sf-8-B-score for '${fieldToString(fieldB)}: ${scoreB}`);
281
- if (scoreA === scoreB) {
282
- return 0;
283
- }
284
- if (scoreB === 0) {
285
- return 1;
286
- }
287
- if (scoreA === 0) {
288
- return -1;
289
- }
290
- if (scoreA > scoreB) { // smaller is better
291
- return 1;
292
- }
239
+ function sortByFieldLinkAndSequenceNumber(fieldA, fieldB) { // Sort by subfield $8 that is...
240
+ const scoreA = fieldGetMinLinkAndSequenceNumber(fieldA);
241
+ const scoreB = fieldGetMinLinkAndSequenceNumber(fieldB);
242
+ //debugDev(` sf-8-A-score for '${fieldToString(fieldA)}: ${scoreA}`);
243
+ //debugDev(` sf-8-B-score for '${fieldToString(fieldB)}: ${scoreB}`);
244
+ if (scoreA === scoreB) {
245
+ return 0;
246
+ }
247
+ if (scoreB === 0) {
248
+ return 1;
249
+ }
250
+ if (scoreA === 0) {
293
251
  return -1;
294
252
  }
253
+ if (scoreA > scoreB) { // smaller is better
254
+ return 1;
255
+ }
256
+ return -1;
257
+ }
295
258
 
296
- function sortByOccurrenceNumber(fieldA, fieldB) { // Sort by subfield $6
297
259
 
298
- function fieldGetOccurrenceNumber(field) { // should this function be exported? (based on validator sortRelatorFields.js)
299
- if (!field.subfields) {
300
- return 0;
301
- }
302
- const subfield6 = field.subfields.find(sf => isValidSubfield6(sf));
303
- if (subfield6 === undefined) {
304
- return 0;
305
- }
306
- return parseInt(subfield6GetOccurrenceNumber(subfield6), 10);
307
- }
260
+ function sortByOccurrenceNumber(fieldA, fieldB) { // Sort by subfield $6
308
261
 
309
- if (fieldA.tag !== '880') {
262
+ function fieldGetOccurrenceNumber(field) { // should this function be exported? (based on validator sortRelatorFields.js)
263
+ if (!field.subfields) {
310
264
  return 0;
311
265
  }
312
- const scoreA = fieldGetOccurrenceNumber(fieldA);
313
- const scoreB = fieldGetOccurrenceNumber(fieldB);
314
-
315
- //debugDev(`A: '${fieldToString(fieldA)}: ${scoreA}`);
316
- //debugDev(`B: '${fieldToString(fieldB)}: ${scoreB}`);
317
-
318
- if (scoreA === scoreB) {
266
+ const subfield6 = field.subfields.find(sf => isValidSubfield6(sf));
267
+ if (subfield6 === undefined) {
319
268
  return 0;
320
269
  }
321
- if (scoreB === 0) {
322
- return -1;
323
- }
324
- if (scoreA === 0) {
325
- return 1;
326
- }
327
- if (scoreA > scoreB) { // smaller is better
328
- return 1;
329
- }
330
- return -1;
270
+ return parseInt(subfield6GetOccurrenceNumber(subfield6), 10);
331
271
  }
332
272
 
333
- function sortAlphabetically(fieldA, fieldB) {
334
- const tagToSortingSubfields = {
335
- // '028': ['b', 'a']?
336
- 'LOW': ['a'],
337
- 'SID': ['c']
338
- };
339
-
340
- function scoreSubfieldsAlphabetically(setOfSubfields) {
341
- if (setOfSubfields.length === 0) {
342
- return 0;
343
- }
344
- const [subfieldCode, ...remainingSubfieldCodes] = setOfSubfields;
345
- const valA = selectFirstValue(fieldA, subfieldCode);
346
- const valB = selectFirstValue(fieldB, subfieldCode);
347
- //debugDev(`CHECKING SUBFIELD '${subfieldCode}'`);
348
- if (!valA) {
349
- if (!valB) {
350
- return scoreSubfieldsAlphabetically(remainingSubfieldCodes);
351
- }
352
- return -1;
353
- }
354
- if (!valB) {
355
- return 1;
356
- }
357
- //debugDev(`CHECKING SUBFIELD '${subfieldCode}': '${valA}' vs '${valB}'`);
358
-
359
- if (valA < valB) {
360
- return -1;
361
- }
362
- if (valB < valA) {
363
- return 1;
364
- }
365
- return scoreSubfieldsAlphabetically(remainingSubfieldCodes);
366
- }
367
-
368
- if (fieldA.tag === fieldB.tag) {
369
- if (!(fieldA.tag in tagToSortingSubfields)) {
370
- return 0;
371
- }
273
+ if (fieldA.tag !== '880') {
274
+ return 0;
275
+ }
276
+ const scoreA = fieldGetOccurrenceNumber(fieldA);
277
+ const scoreB = fieldGetOccurrenceNumber(fieldB);
372
278
 
373
- const subfieldsToCheck = tagToSortingSubfields[fieldA.tag];
279
+ //debugDev(`A: '${fieldToString(fieldA)}: ${scoreA}`);
280
+ //debugDev(`B: '${fieldToString(fieldB)}: ${scoreB}`);
374
281
 
375
- //debugDev(`CHECKING ${subfieldsToCheck.join(', ')}`);
376
- const result = scoreSubfieldsAlphabetically(subfieldsToCheck);
377
- //debugDev(`RESULT ${result}`);
378
- return result;
379
- }
282
+ if (scoreA === scoreB) {
380
283
  return 0;
381
284
  }
382
-
383
- //-----------------------------------------------------------------------------
285
+ if (scoreB === 0) {
286
+ return -1;
287
+ }
288
+ if (scoreA === 0) {
289
+ return 1;
290
+ }
291
+ if (scoreA > scoreB) { // smaller is better
292
+ return 1;
293
+ }
294
+ return -1;
295
+ }
384
296
 
385
297
 
386
- function selectFirstValue(field, subcode) {
387
- return field.subfields
388
- .filter(subfield => subcode === subfield.code)
389
- .map(subfield => subfield.value)
390
- .slice(0, 1);
391
- }
298
+ function selectFirstValue(field, subcode) {
299
+ return field.subfields
300
+ .filter(subfield => subcode === subfield.code)
301
+ .map(subfield => subfield.value)
302
+ .slice(0, 1);
392
303
  }
393
304
 
@@ -0,0 +1,6 @@
1
+ {
2
+ "message" : [
3
+ "'TODO: 521 ## ‡a Diiba - Daaba ― Doh – Doh-Too' => '521 ## ‡a Diiba - Daaba - Doh - Doh-Too'"
4
+ ],
5
+ "valid": false
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "description": "Validate dash normalization: fails as it contains dashes of various lenghts",
3
+ "enabled": true,
4
+ "fix": false,
5
+ "only": false
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "fields" : [
3
+ { "tag": "005", "value": "20220202020202.0" },
4
+ { "tag": "521", "ind1": " ", "ind2": " ", "subfields" : [
5
+ { "code": "a", "value": "Diiba - Daaba \u2015 Doh \u2013 Doh-Too" }
6
+ ]}
7
+ ]
8
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "leader": "",
3
+ "fields" : [
4
+ { "tag": "005", "value": "20220202020202.0" },
5
+ { "tag": "500", "ind1": " ", "ind2": " ", "subfields" : [
6
+ { "code": "a", "value": "Diiba - Daaba - Doh - Doh-Too" }
7
+ ]}
8
+ ],
9
+ "_validationOptions": {}
10
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description": "Fix field 521 dashes",
3
+ "enabled": true,
4
+ "fix": true
5
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "fields" : [
3
+ { "tag": "005", "value": "20220202020202.0" },
4
+ { "tag": "500", "ind1": " ", "ind2": " ", "subfields" : [
5
+ { "code": "a", "value": "Diiba - Daaba \u2015 Doh \u2013 Doh-Too" }
6
+ ]}
7
+ ]
8
+ }