@natlibfi/marc-record-validators-melinda 10.13.1 → 10.14.0-alpha.2

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 (69) hide show
  1. package/dist/ending-punctuation-conf.js +3 -6
  2. package/dist/ending-punctuation-conf.js.map +1 -1
  3. package/dist/index.js +7 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/punctuation/rules/aut.js +1 -2
  6. package/dist/punctuation/rules/aut.js.map +1 -1
  7. package/dist/punctuation/rules/bib.js +1 -2
  8. package/dist/punctuation/rules/bib.js.map +1 -1
  9. package/dist/punctuation2.js +40 -14
  10. package/dist/punctuation2.js.map +1 -1
  11. package/dist/removeInferiorDataFields.js +166 -168
  12. package/dist/removeInferiorDataFields.js.map +1 -1
  13. package/dist/sortFields.js +383 -0
  14. package/dist/sortFields.js.map +1 -0
  15. package/dist/sortFields.spec.js +51 -0
  16. package/dist/sortFields.spec.js.map +1 -0
  17. package/dist/sortRelatorTerms.js +3 -25
  18. package/dist/sortRelatorTerms.js.map +1 -1
  19. package/dist/sortSubfields.js +14 -4
  20. package/dist/sortSubfields.js.map +1 -1
  21. package/dist/sortSubfields.spec.js +4 -3
  22. package/dist/sortSubfields.spec.js.map +1 -1
  23. package/dist/utils.js +4 -0
  24. package/dist/utils.js.map +1 -1
  25. package/package.json +7 -7
  26. package/src/index.js +3 -1
  27. package/src/punctuation2.js +14 -7
  28. package/src/removeInferiorDataFields.js +166 -169
  29. package/src/sortFields.js +393 -0
  30. package/src/sortFields.spec.js +52 -0
  31. package/src/sortRelatorTerms.js +3 -25
  32. package/src/sortSubfields.js +18 -4
  33. package/src/sortSubfields.spec.js +3 -3
  34. package/src/utils.js +5 -0
  35. package/test-fixtures/remove-inferior-datafields/f01/expectedResult.json +12 -0
  36. package/test-fixtures/remove-inferior-datafields/f01/record.json +19 -0
  37. package/test-fixtures/sort-fields/01/input.json +32 -0
  38. package/test-fixtures/sort-fields/01/metadata.json +5 -0
  39. package/test-fixtures/sort-fields/01/result.json +29 -0
  40. package/test-fixtures/sort-fields/02/input.json +49 -0
  41. package/test-fixtures/sort-fields/02/metadata.json +4 -0
  42. package/test-fixtures/sort-fields/02/result.json +50 -0
  43. package/test-fixtures/sort-fields/03/input.json +20 -0
  44. package/test-fixtures/sort-fields/03/metadata.json +5 -0
  45. package/test-fixtures/sort-fields/03/result.json +21 -0
  46. package/test-fixtures/sort-fields/04/input.json +13 -0
  47. package/test-fixtures/sort-fields/04/metadata.json +4 -0
  48. package/test-fixtures/sort-fields/04/result.json +13 -0
  49. package/test-fixtures/sort-fields/05/input.json +27 -0
  50. package/test-fixtures/sort-fields/05/metadata.json +4 -0
  51. package/test-fixtures/sort-fields/05/result.json +28 -0
  52. package/test-fixtures/sort-fields/06/input.json +36 -0
  53. package/test-fixtures/sort-fields/06/metadata.json +4 -0
  54. package/test-fixtures/sort-fields/06/result.json +37 -0
  55. package/test-fixtures/sort-fields/07/input.json +21 -0
  56. package/test-fixtures/sort-fields/07/metadata.json +4 -0
  57. package/test-fixtures/sort-fields/07/result.json +22 -0
  58. package/test-fixtures/sort-fields/08/input.json +29 -0
  59. package/test-fixtures/sort-fields/08/metadata.json +5 -0
  60. package/test-fixtures/sort-fields/08/result.json +29 -0
  61. package/test-fixtures/sort-fields/09/input.json +41 -0
  62. package/test-fixtures/sort-fields/09/metadata.json +5 -0
  63. package/test-fixtures/sort-fields/09/result.json +42 -0
  64. package/test-fixtures/sort-fields/10/input.json +54 -0
  65. package/test-fixtures/sort-fields/10/metadata.json +5 -0
  66. package/test-fixtures/sort-fields/10/result.json +53 -0
  67. package/test-fixtures/sort-subfields/f02/expectedResult.json +26 -0
  68. package/test-fixtures/sort-subfields/f02/metadata.json +7 -0
  69. package/test-fixtures/sort-subfields/f02/record.json +25 -0
@@ -1,7 +1,7 @@
1
1
  import createDebugLogger from 'debug';
2
2
  import {fieldToChain, sameField} from './removeDuplicateDataFields';
3
3
  import {fieldGetOccurrenceNumberPairs, fieldHasValidSubfield6, fieldSevenToOneOccurrenceNumber, fieldsToNormalizedString} from './subfield6Utils';
4
- import {fieldsToString, fieldToString, nvdebug} from './utils';
4
+ import {fieldHasSubfield, fieldsToString, fieldToString, nvdebug, uniqArray} from './utils';
5
5
  import {fieldHasValidSubfield8} from './subfield8Utils';
6
6
  import {encodingLevelIsBetterThanPrepublication, getEncodingLevel} from './prepublicationUtils';
7
7
  import {cloneAndNormalizeFieldForComparison} from './normalizeFieldForComparison';
@@ -42,49 +42,56 @@ export default function () {
42
42
 
43
43
 
44
44
  function deriveInferiorChains(fields, record) {
45
- /* eslint-disable */
46
- let deletableStringsObject = {};
45
+ //nvdebug(`======= GOT ${fields.length} FIELDS TO CHAINIFY`);
46
+ const hash = {};
47
47
 
48
- nvdebug(`WP1: GOT ${fields.length} field(s) for potential deletable chain derivation`);
49
- fields.forEach(field => fieldDeriveChainDeletables(field));
48
+ fields.forEach(f => fieldToChainToDeletables(f));
50
49
 
51
- function fieldDeriveChainDeletables(field) {
50
+ return hash;
51
+
52
+ //nvdebug(`WP1: GOT ${todoList.length} CHAINS`);
53
+
54
+
55
+ // here we map deletableStringObject[str] => field. The idea is to help debugging. We don't actually need the field object...
56
+ //return deriveChainDeletables(todoList);
57
+
58
+ function fieldToChainToDeletables(field) {
52
59
  const chain = fieldToChain(field, record);
53
- if (chain.length === 0) {
60
+ if (chain.length < 2) {
54
61
  return;
55
62
  }
56
63
  const chainAsString = fieldsToNormalizedString(chain, 0, true, true);
64
+ const arr = deriveChainDeletables([chainAsString]);
65
+ //nvdebug(`GOT ${arr.length} DELETABLES FOR ${chainAsString}`);
66
+ arr.forEach(val => {
67
+ if (!(val in hash)) { // eslint-disable-line functional/no-conditional-statements
68
+ hash[val] = field; // eslint-disable-line functional/immutable-data
69
+ }
70
+ });
71
+ }
57
72
 
58
- //nvdebug(`666: ${chainAsString}`);
73
+ function deriveChainDeletables(todoList, deletables = []) {
74
+ const [chainAsString, ...stillToDo] = todoList;
75
+ if (chainAsString === undefined) {
76
+ return deletables;
77
+ }
59
78
 
60
79
  // Fix MRA-476 (part 1): one $6 value can be worse than the other
61
- let tmp = chainAsString;
62
- while (tmp.match(/ ‡6 [0-9X][0-9][0-9]-(?:XX|[0-9]+)\/[^ ]+/u)) {
63
- tmp = tmp.replace(/( ‡6 [0-9X][0-9][0-9]-(?:XX|[0-9]+))\/[^ ]+/u, '$1');
64
- //nvdebug(`FFS: ${tmp}`);
65
- deletableStringsObject[tmp] = field;
66
- }
80
+ const withoutScriptIdentificationCode = chainAsString.replace(/( ‡6 [0-9X][0-9][0-9]-(?:XX|[0-9]+))\/[^ ]+/u, '$1'); // eslint-disable-line prefer-named-capture-group
67
81
 
68
82
  // Remove keepless versions:
69
- tmp = chainAsString;
70
- while (tmp.match(/ ‡9 [A-Z]+<KEEP>/)) {
71
- tmp = tmp.replace(/ ‡9 [A-Z]+<KEEP>/, '');
72
- deletableStringsObject[tmp] = field;
73
- //nvdebug(`FFS: ${tmp}`);
74
- }
83
+ const keepless = chainAsString.replace(/ ‡9 [A-Z]+<KEEP>/u, '');
75
84
 
76
85
  // MRA-433: 490 ind1=1 vs ind1=0: remove latter (luckily no 2nd indicator etc)
77
- if (chainAsString.match(/^490 1 .*\t880 1 ‡/) ) {
78
- // change ind1s to '0' to get the deletable chain:
79
- tmp = chainAsString.replace(/^490 1/u, '490 0').replace(/\t880 1/ug, "\t880 0");
80
- deletableStringsObject[tmp] = field;
86
+ const linked490Ind1 = chainAsString.replace(/^490 1/u, '490 0').replace(/\t880 1/ug, '\t880 0');
87
+ const arr = [withoutScriptIdentificationCode, keepless, linked490Ind1].filter(val => val !== chainAsString);
88
+ if (arr.length > 0) {
89
+ return deriveChainDeletables([...stillToDo, ...arr], [...deletables, ...arr]);
81
90
  }
82
91
 
92
+ return deriveChainDeletables(stillToDo, deletables);
83
93
  }
84
94
 
85
-
86
- /* eslint-enable */
87
- return deletableStringsObject;
88
95
  }
89
96
 
90
97
  function isRelevantChain6(field, record) {
@@ -102,13 +109,8 @@ function isRelevantChain6(field, record) {
102
109
  return false;
103
110
  }
104
111
 
105
- // Chainwise non-initial fields are not relevant as chains is handled through the initial/head field
106
- /* eslint-disable */
107
- field.tmpInferiorId = 666;
108
- const result = chain[0].tmpInferiorId === 666 ? true : false;
109
- delete field.tmpInferiorId;
110
- /* eslint-enable */
111
- return result;
112
+ // Check whether our field is the head of a chain:
113
+ return sameField(field, chain[0]);
112
114
  }
113
115
 
114
116
  export function removeInferiorChains(record, fix = true) {
@@ -122,13 +124,43 @@ export function removeInferiorChains(record, fix = true) {
122
124
  return [];
123
125
  }
124
126
 
125
- nvdebug(`removeInferiorChains() has ${fields.length} fields-in-chain(s), and a list of ${nChains} deletable(s)`);
127
+ //nvdebug(`removeInferiorChains() has ${fields.length} fields-in-chain(s), and a list of ${nChains} deletable(s)`);
128
+
129
+ return innerRemoveInferiorChains(fields);
130
+
131
+ function innerRemoveInferiorChains(fields, deletedStringsArray = []) {
132
+ const [currField, ...remainingFields] = fields;
133
+
134
+ if (currField === undefined) {
135
+ return deletedStringsArray;
136
+ }
126
137
 
138
+ const chain = fieldToChain(currField, record);
139
+ if (chain.length === 0 || !sameField(currField, chain[0])) {
140
+ return innerRemoveInferiorChains(remainingFields, deletedStringsArray);
141
+ }
127
142
 
128
- /* eslint-disable */
143
+ const chainAsString = fieldsToNormalizedString(chain, 0, true, true);
144
+ if (!(chainAsString in deletableChainsAsKeys)) {
145
+ return innerRemoveInferiorChains(remainingFields, deletedStringsArray);
146
+ }
129
147
 
130
- let deletedStringsArray = [];
131
- fields.forEach(f => innerRemoveInferiorChain(f));
148
+ const triggeringField = deletableChainsAsKeys[chainAsString];
149
+ const triggeringChain = fieldToChain(triggeringField, record);
150
+
151
+ // If the inferior (deletable) chain is 1XX-based, convert the triggering better chain from 7XX to 1XX:
152
+ if (chainContains1XX(chain)) { // eslint-disable-line functional/no-conditional-statements
153
+ triggeringChain.forEach(f => sevenToOne(f, triggeringChain));
154
+ }
155
+ //nvdebug(`iRIS6C: ${chainAsString}`);
156
+ const deletedString = fieldsToString(chain);
157
+ const message = `DEL: '${deletedString}' REASON: '${fieldsToString(triggeringChain)}'`;
158
+ if (fix) { // eslint-disable-line functional/no-conditional-statements
159
+ //nvdebug(`INFERIOR $6 CHAIN REMOVAL: ${message}}`, debug);
160
+ chain.forEach(field => record.removeField(field));
161
+ }
162
+ return innerRemoveInferiorChains(remainingFields, [...deletedStringsArray, message]);
163
+ }
132
164
 
133
165
  function chainContains1XX(chain) {
134
166
  return chain.some(f => f.tag.substring(0, 1) === '1');
@@ -146,150 +178,116 @@ export function removeInferiorChains(record, fix = true) {
146
178
  pairs.forEach(pairedField => fieldSevenToOneOccurrenceNumber(pairedField));
147
179
  }
148
180
 
149
- function innerRemoveInferiorChain(field) {
150
- const chain = fieldToChain(field, record);
151
- if (chain.length === 0 || !sameField(field, chain[0])) {
152
- return;
153
- }
181
+ }
154
182
 
155
- const chainAsString = fieldsToNormalizedString(chain, 0, true, true);
156
- if (chainAsString in deletableChainsAsKeys) {
157
- const triggeringField = deletableChainsAsKeys[chainAsString];
158
- const triggeringChain = fieldToChain(triggeringField, record);
159
183
 
160
- // If the inferior (deletable) chain is 1XX-based, convert the triggering better chain from 7XX to 1XX:
161
- if(chainContains1XX(chain)) {
162
- triggeringChain.forEach(f => sevenToOne(f, triggeringChain));
163
- }
164
- //nvdebug(`iRIS6C: ${chainAsString}`);
165
- const deletedString = fieldsToString(chain);
166
- const message = `DEL: '${deletedString}' REASON: '${fieldsToString(triggeringChain)}'`;
167
- deletedStringsArray.push(message);
168
- if (fix) {
169
- nvdebug(`INFERIOR $6 CHAIN REMOVAL: ${message}}`, debug);
170
- chain.forEach(currField => record.removeField(currField));
171
- }
172
- }
173
- }
184
+ function getIdentifierlessAndKeeplessSubsets(fieldAsString) {
185
+ // The rules below are not perfect (in complex cases they don't catch all permutations), but good enough:
186
+ // Remove identifier(s) (MELKEHITYS-2383-ish):
187
+
188
+ const identifierlessString = fieldAsString.replace(/ ‡[01] [^‡]+($| ‡)/u, '$1'); // eslint-disable-line prefer-named-capture-group
189
+ const keeplessString = fieldAsString.replace(/ ‡9 [A-Z]+<KEEP>/u, '');
174
190
 
175
- /* eslint-enable */
176
- return deletedStringsArray;
191
+ return [identifierlessString, keeplessString].filter(val => val !== fieldAsString);
177
192
  }
178
193
 
179
- function deriveIndividualDeletables490(fieldAsString) {
194
+ function deriveIndividualDeletables490(todoList, deletables = []) {
195
+ const [fieldAsString, ...stillToDo] = todoList;
196
+ if (fieldAsString === undefined) {
197
+ return deletables;
198
+ }
199
+ //nvdebug(`PROCESS ${fieldAsString}`);
180
200
  if (!fieldAsString.match(/^490/u)) {
181
- return [];
201
+ return deriveIndividualDeletables490(stillToDo, deletables);
182
202
  }
183
203
 
184
- /* eslint-disable */
185
- let deletable490s = [];
186
-
187
204
  // $6-less version (keep this first)
188
- let tmp = fieldAsString.replace(/ ‡6 [^‡]+ ‡/u, ' ‡');
189
- if ( tmp !== fieldAsString) {
190
- fieldAsString = tmp; // NB! Carry on with $6-less version!
191
- deletable490s.push(tmp);
192
- }
205
+ const sixless = fieldAsString.replace(/ ‡6 [^‡]+ ‡/u, ' ‡');
193
206
 
194
207
  // Without final $v or $x:
195
- tmp = fieldAsString.replace(/ *[;,] ‡[vx] [^‡]+$/u, '');
196
- if ( tmp !== fieldAsString) {
197
- deletable490s.push(tmp);
198
- }
208
+ const withoutFinalVOrX = fieldAsString.replace(/ *[;,] ‡[vx] [^‡]+$/u, '');
209
+ // Add intermediate $x-less version
210
+ const xless = fieldAsString.replace(/, ‡x [^‡]+(, ‡x| ; ‡v)/u, '$1'); // eslint-disable-line prefer-named-capture-group
199
211
 
200
- // Add intermedia $x-less version
201
- tmp = fieldAsString.replace(/, ‡x [^‡]+(,x| ; ‡v)/u, '$1');
202
- // Add final $v/$x-less version
203
- if ( tmp !== fieldAsString) {
204
- deletable490s.push(tmp);
205
- }
206
-
207
- // Add $xv-less version
208
- tmp = fieldAsString.replace(/, ‡x [^‡]+ ‡v [^‡]+$/u, '');
209
- if ( tmp !== fieldAsString) {
210
- deletable490s.push(tmp);
211
- }
212
+ // Add $xv-less version (handled by recursion?)
213
+ const xvless = fieldAsString.replace(/, ‡x [^‡]+ ‡v [^‡]+$/u, '');
212
214
 
213
215
  // MRA-433-ish (non-chain): 490 ind1=1 vs ind1=0: remove latter
214
- if (fieldAsString.match(/^490 1/) ) {
215
- // TODO: $x-less and $v-less versions...
216
- tmp = `490 0${fieldAsString.substring(5)}`;
217
- deletable490s.push(tmp);
218
- const arr = deriveIndividualDeletables490(tmp);
219
- arr.forEach(val => deletable490s.push(val));
220
- }
216
+ const modifiedInd2 = fieldAsString.match(/^490 1/u) ? `490 0${fieldAsString.substring(5)}` : fieldAsString;
217
+
218
+ const arr = [sixless, withoutFinalVOrX, xless, xvless, modifiedInd2].filter(val => val !== fieldAsString);
221
219
 
222
- nvdebug(`${deletable490s.length} derivation(s) for ${fieldAsString}`);
223
- if (deletable490s.length > 0) {
224
- nvdebug(deletable490s.join('\n'));
220
+ /*
221
+ if (arr.length) { // eslint-disable-line functional/no-conditional-statements
222
+ nvdebug(`${arr.length} derivation(s) for ${fieldAsString}`);
223
+ nvdebug(arr.join('\n'));
225
224
  }
226
- /* eslint-enable */
227
- return deletable490s;
225
+ */
226
+ return arr;
228
227
  }
229
228
 
230
229
  function deriveIndividualDeletables(record) {
231
- /* eslint-disable */
232
- let deletableStringsArray = [];
230
+ const todoList = record.fields.map(f => fieldToString(f));
231
+ //const finishedRecord = encodingLevelIsBetterThanPrepublication(getEncodingLevel(record));
233
232
 
234
- const finishedRecord = encodingLevelIsBetterThanPrepublication(getEncodingLevel(record));
233
+ const deletableStringsArray = processTodoList(todoList);
235
234
 
236
- record.fields.forEach(field => fieldDeriveIndividualDeletables(field));
235
+ return uniqArray(deletableStringsArray);
237
236
 
238
- function fieldDeriveIndividualDeletables(field) {
239
- const fieldAsString = fieldToString(field);
237
+ function processTodoList(thingsToDo, deletables = []) {
238
+ const [currString, ...stillToDo] = thingsToDo;
239
+
240
+ if (currString === undefined) {
241
+ return deletables;
242
+ }
240
243
 
241
- // Proof-of-concept rule:
242
- let tmp = fieldAsString;
243
- if (field.tag.match(/^[1678]00$/u)) {
244
- while (tmp.match(/, ‡e [^‡]+\.$/)) {
245
- tmp = tmp.replace(/, ‡e [^‡]+\.$/, '.');
246
- deletableStringsArray.push(tmp);
244
+ if (currString.match(/^[1678]00/u)) {
245
+ // Proof-of-concpet rule. Should be improved eventually...
246
+ if (currString.match(/, ‡e [^‡]+\.$/u)) {
247
+ const tmp = currString.replace(/, ‡e [^‡]+\.$/u, '.');
248
+ return processTodoList([tmp, ...stillToDo], [...deletables, tmp]);
247
249
  }
248
250
  }
249
251
 
250
- if (field.tag === '505') { // MRA-413-ish
251
- if (fieldAsString.match(/^.0.*-- ‡t/u)) {
252
- tmp = fieldAsString;
253
- tmp = tmp.replace(/ -- ‡t /gu, ' -- ');
254
- tmp = tmp.replace(/ ‡[rg] /gu, ' ');
255
- tmp = tmp.replace(/ ‡t /u, ' ‡a '); // first $t, not
256
- tmp = tmp.replace(/^505 (.)0/u, '505 $1#');
257
- if (tmp !== fieldAsString) {
258
- deletableStringsArray.push(tmp);
259
- }
260
- //nvdebug(`505 ORIGINAL: '${fieldAsString}'`)
261
- //nvdebug(`505 DERIVATE: '${tmp}'`)
252
+ if (currString.match(/^505 .0.*-- ‡t/u)) { // MRA-413-ish
253
+ const tmp = currString.replace(/ -- ‡t /gu, ' -- '). // remove non-initial $t subfields
254
+ replace(/ ‡[rg] /gu, ' '). // remove $r and $g subfields
255
+ replace(/ ‡t /u, ' ‡a '). // change first $t to $a
256
+ // ind2: '1' => '#':
257
+ replace(/^505 (.)0/u, '505 $1#'); // eslint-disable-line prefer-named-capture-group
258
+ if (tmp !== currString) {
259
+ return processTodoList([tmp, ...stillToDo], [...deletables, tmp]);
262
260
  }
261
+ //nvdebug(`505 ORIGINAL: '${fieldAsString}'`)
262
+ //nvdebug(`505 DERIVATE: '${tmp}'`)
263
263
  }
264
264
 
265
265
  // MET-381: remove occurence number TAG-00, if TAG-NN existists
266
- if (field.tag === '880') {
267
- tmp = fieldAsString;
268
- if (tmp.match(/ ‡6 [0-9][0-9][0-9]-(?:[1-9][0-9]|0[1-9])/)) {
269
- tmp = tmp.replace(/( ‡6 [0-9][0-9][0-9])-[0-9]+/, '$1-00');
270
- nvdebug(`MET-381: ADD TO DELETABLES: '${tmp}'`);
271
- deletableStringsArray.push(tmp);
272
- if (tmp.match(/ ‡6 [0-9][0-9][0-9]-00\/[^ ]+ /)) {
273
- tmp = tmp.replace(/( ‡6 [0-9][0-9][0-9]-00)[^ ]+/, '$1');
274
- nvdebug(`MET-381: ADD TO DELETABLES: '${tmp}'`);
275
- deletableStringsArray.push(tmp);
276
- }
266
+ if (currString.match(/^880.* ‡6 [0-9][0-9][0-9]-(?:[1-9][0-9]|0[1-9])/u)) {
267
+ const tmp = currString.replace(/( ‡6 [0-9][0-9][0-9])-[0-9]+/u, '$1-00'); // eslint-disable-line prefer-named-capture-group
268
+ //nvdebug(`MET-381: ADD TO DELETABLES: '${tmp}'`);
269
+ //deletableStringsArray.push(tmp);
270
+ if (tmp.match(/ ‡6 [0-9][0-9][0-9]-00\/[^ ]+ /u)) {
271
+ const tmp2 = tmp.replace(/( ‡6 [0-9][0-9][0-9]-00)[^ ]+/u, '$1'); // eslint-disable-line prefer-named-capture-group
272
+ //nvdebug(`MET-381: ADD TO DELETABLES: '${tmp2}'`);
273
+ return processTodoList(stillToDo, [...deletables, tmp, tmp2]);
277
274
  }
275
+ return processTodoList(stillToDo, [...deletables, tmp]);
278
276
  }
279
277
 
280
- const d490 = deriveIndividualDeletables490(fieldAsString);
281
- d490.forEach(str => deletableStringsArray.push(str));
278
+ const d490 = deriveIndividualDeletables490([currString]);
279
+ if (d490.length) {
280
+ return processTodoList([...stillToDo, ...d490], [...deletables, ...d490]);
281
+ }
282
+ // d490.forEach(str => deletables.push(str)); // eslint-disable-line functional/immutable-data
282
283
 
283
- // Remove keepless versions:
284
- tmp = fieldAsString;
285
- while (tmp.match(/ ‡9 [A-Z]+<KEEP>/)) {
286
- tmp = tmp.replace(/ ‡9 [A-Z]+<KEEP>/u, '');
287
- deletableStringsArray.push(tmp);
284
+ const subsets = getIdentifierlessAndKeeplessSubsets(currString); // eslint-disable-line no-param-reassign
285
+ if (subsets.length) {
286
+ return processTodoList([...stillToDo, ...subsets], [...deletables, ...subsets]);
288
287
  }
289
288
 
289
+ return processTodoList(stillToDo, deletables);
290
290
  }
291
- /* eslint-enable */
292
- return deletableStringsArray; // we should do uniq!
293
291
 
294
292
  }
295
293
 
@@ -298,32 +296,31 @@ function fieldToNormalizedString(field) {
298
296
  return fieldToString(normalizedField);
299
297
  }
300
298
 
301
- function deriveIndividualNormalizedDeletables(record) {
302
- /* eslint-disable */
303
- let deletableNormalizedStringsArray = [];
304
-
299
+ function deriveIndividualNormalizedDeletables(record) { // MET-461:
305
300
  const recordIsFinished = encodingLevelIsBetterThanPrepublication(getEncodingLevel(record));
301
+ if (!recordIsFinished) {
302
+ return [];
303
+ }
304
+ const relevantFields = record.fields.filter(f => ['245', '246'].includes(f.tag) && fieldHasSubfield(f, 'a'));
306
305
 
307
- record.fields.forEach(field => fieldDeriveIndividualNormalizedDeletables(field));
306
+ return deriveDeletable946s(relevantFields);
308
307
 
309
- function fieldDeriveIndividualNormalizedDeletables(field) {
310
- const fieldAsNormalizedString = fieldToNormalizedString(field);
311
- let tmp = fieldAsNormalizedString;
312
-
313
- // MET-461:
314
- if (recordIsFinished && ['245', '246'].includes(field.tag) && fieldAsNormalizedString.match(/ ‡a /u)) {
315
- tmp = fieldAsNormalizedString;
316
- tmp = tmp.replace(/^(...) ../u, '946 ##'); // Ind
317
- tmp = tmp.replace(" ‡a ", " ‡i nimeke onixissa ‡a "); // NB! This is added in the normalized lower-cased form!
318
- tmp = tmp.replace(/(?: \/)? ‡c[^‡]+$/u, ''); // Can $c be non-last?
319
- deletableNormalizedStringsArray.push(tmp);
320
- deletableNormalizedStringsArray.push(`${tmp} ‡5 MELINDA`); // MET-461 comment. NB! $5 is never normalized
308
+ function deriveDeletable946s(fields, results = []) {
309
+ const [currField, ...remainingFields] = fields;
310
+ if (currField === undefined) {
311
+ return results;
321
312
  }
322
- }
323
-
324
- /* eslint-enable */
325
- return deletableNormalizedStringsArray; // we should do uniq!
326
313
 
314
+ const fieldAsNormalizedString = fieldToNormalizedString(currField);
315
+ const tmp = fieldAsNormalizedString.replace(/^(?:...) ../u, '946 ##'). // <= Change tag to 946 and indicators to '##'
316
+ replace(' ‡a ', ' ‡i nimeke onixissa ‡a '). // Add $i before $a. NB! This is added in the normalized lower-cased form!
317
+ replace(/(?: \/)? ‡c[^‡]+$/u, ''); // Remove $c. (Can $c be non-last?)
318
+ const candArray = [tmp, `${tmp} ‡5 MELINDA`].filter(val => val !== fieldAsNormalizedString);
319
+ if (candArray.length) {
320
+ return deriveDeletable946s(remainingFields, [...results, ...candArray]);
321
+ }
322
+ return deriveDeletable946s(remainingFields, results);
323
+ }
327
324
  }
328
325
 
329
326
  export function removeIndividualInferiorDatafields(record, fix = true) { // No $6 nor $8 in field
@@ -339,7 +336,7 @@ export function removeIndividualInferiorDatafields(record, fix = true) { // No $
339
336
 
340
337
  if (fix) { // eslint-disable-line functional/no-conditional-statements
341
338
  hits.forEach(field => {
342
- nvdebug(`Remove inferior field: ${fieldToString(field)}`);
339
+ //nvdebug(`Remove inferior field: ${fieldToString(field)}`, debug);
343
340
  record.removeField(field);
344
341
  });
345
342
  }
@@ -367,8 +364,8 @@ export function removeInferiorDatafields(record, fix = true) {
367
364
  const removables6 = removeInferiorChains(record, fix); // Lone subfield $6 chains
368
365
  // HOW TO HANDLE $6+$8 combos? Skipping is relatively OK.
369
366
 
370
- nvdebug(`REMOVABLES:\n ${removables.join('\n ')}`);
371
- nvdebug(`REMOVABLES 6:\n ${removables6.join('\n ')}`);
367
+ nvdebug(`REMOVABLES:\n ${removables.join('\n ')}`, debug);
368
+ nvdebug(`REMOVABLES 6:\n ${removables6.join('\n ')}`, debug);
372
369
 
373
370
  const removablesAll = removables.concat(removables6); //.concat(removables8);
374
371