csv-sort 6.0.5 → 6.0.6

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.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @name csv-sort
3
3
  * @fileoverview Sorts double-entry bookkeeping CSV coming from internet banking
4
- * @version 6.0.5
4
+ * @version 6.0.6
5
5
  * @author Roy Revelt, Codsen Ltd
6
6
  * @license MIT
7
7
  * {@link https://codsen.com/os/csv-sort/}
@@ -12,268 +12,435 @@ import { splitEasy } from 'csv-split-easy';
12
12
  import currency from 'currency.js';
13
13
 
14
14
  function isNumeric(str) {
15
- if (!str.trim()) {
16
- return false;
17
- }
18
- return Number(str) === Number(str);
15
+ if (!str.trim()) {
16
+ return false;
17
+ }
18
+ return Number(str) === Number(str);
19
19
  }
20
- const currencySigns = ["د.إ", "؋", "L", "֏", "ƒ", "Kz", "$", "ƒ", "₼", "KM", "৳", "лв", ".د.ب", "FBu", "$b", "R$", "฿", "Nu.", "P", "p.", "BZ$", "FC", "CHF", "¥", "₡", "₱", "Kč", "Fdj", "kr", "RD$", "دج", "kr", "Nfk", "Br", "Ξ", "€", "₾", "₵", "GH₵", "D", "FG", "Q", "L", "kn", "G", "Ft", "Rp", "₪", "₹", "ع.د", "﷼", "kr", "J$", "JD", "¥", "KSh", "лв", "៛", "CF", "₩", "₩", "KD", "лв", "₭", "₨", "M", "Ł", "Lt", "Ls", "LD", "MAD", "lei", "Ar", "ден", "K", "₮", "MOP$", "UM", "₨", "Rf", "MK", "RM", "MT", "₦", "C$", "kr", "₨", "﷼", "B/.", "S/.", "K", "₱", "₨", "zł", "Gs", "﷼", "¥", "lei", "Дин.", "₽", "R₣", "﷼", "₨", "ج.س.", "kr", "£", "Le", "S", "Db", "E", "฿", "SM", "T", "د.ت", "T$", "₤", "₺", "TT$", "NT$", "TSh", "₴", "USh", "$U", "лв", "Bs", "₫", "VT", "WS$", "FCFA", "Ƀ", "CFA", "₣", "﷼", "R", "Z$"];
20
+ const currencySigns = [
21
+ "د.إ",
22
+ "؋",
23
+ "L",
24
+ "֏",
25
+ "ƒ",
26
+ "Kz",
27
+ "$",
28
+ "ƒ",
29
+ "₼",
30
+ "KM",
31
+ "৳",
32
+ "лв",
33
+ ".د.ب",
34
+ "FBu",
35
+ "$b",
36
+ "R$",
37
+ "฿",
38
+ "Nu.",
39
+ "P",
40
+ "p.",
41
+ "BZ$",
42
+ "FC",
43
+ "CHF",
44
+ "¥",
45
+ "₡",
46
+ "₱",
47
+ "Kč",
48
+ "Fdj",
49
+ "kr",
50
+ "RD$",
51
+ "دج",
52
+ "kr",
53
+ "Nfk",
54
+ "Br",
55
+ "Ξ",
56
+ "€",
57
+ "₾",
58
+ "₵",
59
+ "GH₵",
60
+ "D",
61
+ "FG",
62
+ "Q",
63
+ "L",
64
+ "kn",
65
+ "G",
66
+ "Ft",
67
+ "Rp",
68
+ "₪",
69
+ "₹",
70
+ "ع.د",
71
+ "﷼",
72
+ "kr",
73
+ "J$",
74
+ "JD",
75
+ "¥",
76
+ "KSh",
77
+ "лв",
78
+ "៛",
79
+ "CF",
80
+ "₩",
81
+ "₩",
82
+ "KD",
83
+ "лв",
84
+ "₭",
85
+ "₨",
86
+ "M",
87
+ "Ł",
88
+ "Lt",
89
+ "Ls",
90
+ "LD",
91
+ "MAD",
92
+ "lei",
93
+ "Ar",
94
+ "ден",
95
+ "K",
96
+ "₮",
97
+ "MOP$",
98
+ "UM",
99
+ "₨",
100
+ "Rf",
101
+ "MK",
102
+ "RM",
103
+ "MT",
104
+ "₦",
105
+ "C$",
106
+ "kr",
107
+ "₨",
108
+ "﷼",
109
+ "B/.",
110
+ "S/.",
111
+ "K",
112
+ "₱",
113
+ "₨",
114
+ "zł",
115
+ "Gs",
116
+ "﷼",
117
+ "¥",
118
+ "lei",
119
+ "Дин.",
120
+ "₽",
121
+ "R₣",
122
+ "﷼",
123
+ "₨",
124
+ "ج.س.",
125
+ "kr",
126
+ "£",
127
+ "Le",
128
+ "S",
129
+ "Db",
130
+ "E",
131
+ "฿",
132
+ "SM",
133
+ "T",
134
+ "د.ت",
135
+ "T$",
136
+ "₤",
137
+ "₺",
138
+ "TT$",
139
+ "NT$",
140
+ "TSh",
141
+ "₴",
142
+ "USh",
143
+ "$U",
144
+ "лв",
145
+ "Bs",
146
+ "₫",
147
+ "VT",
148
+ "WS$",
149
+ "FCFA",
150
+ "Ƀ",
151
+ "CFA",
152
+ "₣",
153
+ "﷼",
154
+ "R",
155
+ "Z$",
156
+ ];
21
157
  function findType(something) {
22
- /* istanbul ignore next */
23
- if (typeof something !== "string") {
24
- throw new Error(`csv-sort/util/findType(): input must be string! Currently it's: ${typeof something}`);
25
- }
26
- if (isNumeric(something)) {
27
- return "numeric";
28
- }
29
- /* istanbul ignore next */
30
- if (currencySigns.some(singleSign =>
31
- isNumeric(something.replace(singleSign, "").replace(/[,.]/g, "")))) {
32
- return "numeric";
33
- }
34
- if (!something.trim()) {
35
- return "empty";
36
- }
37
- return "text";
158
+ /* istanbul ignore next */
159
+ if (typeof something !== "string") {
160
+ throw new Error(`csv-sort/util/findType(): input must be string! Currently it's: ${typeof something}`);
161
+ }
162
+ if (isNumeric(something)) {
163
+ return "numeric";
164
+ }
165
+ /* istanbul ignore next */
166
+ if (currencySigns.some((singleSign) =>
167
+ isNumeric(something.replace(singleSign, "").replace(/[,.]/g, "")))) {
168
+ return "numeric";
169
+ }
170
+ if (!something.trim()) {
171
+ return "empty";
172
+ }
173
+ return "text";
38
174
  }
39
175
 
40
176
  function sort(input) {
41
- let msgContent = null;
42
- let msgType = null;
43
- if (typeof input !== "string") {
44
- throw new TypeError(`csv-sort/csvSort(): [THROW_ID_01] The input is of a wrong type! We accept either string of array of arrays. We got instead: ${typeof input}, equal to:\n${JSON.stringify(input, null, 4)}`);
45
- } else if (!input.trim()) {
46
- return {
47
- res: [[""]],
48
- msgContent,
49
- msgType
50
- };
51
- }
52
- let content = splitEasy(input);
53
- let schema = [];
54
- let stateHeaderRowPresent = false;
55
- let stateDataColumnRowLengthIsConsistent = true;
56
- const stateColumnsContainingSameValueEverywhere = [];
57
- let indexAtWhichEmptyCellsStart = null;
58
- for (let i = content.length - 1; i >= 0; i--) {
59
- if (!schema.length) {
60
- /* istanbul ignore next */
61
- if (content[i].length !== 1 || content[i][0] !== "") {
62
- for (let y = 0, len = content[i].length; y < len; y++) {
63
- schema.push(findType(content[i][y].trim()));
64
- if (indexAtWhichEmptyCellsStart === null && findType(content[i][y].trim()) === "empty") {
65
- indexAtWhichEmptyCellsStart = y;
66
- }
67
- if (indexAtWhichEmptyCellsStart !== null && findType(content[i][y].trim()) !== "empty") {
68
- indexAtWhichEmptyCellsStart = null;
69
- }
177
+ let msgContent = null;
178
+ let msgType = null;
179
+ if (typeof input !== "string") {
180
+ throw new TypeError(`csv-sort/csvSort(): [THROW_ID_01] The input is of a wrong type! We accept either string of array of arrays. We got instead: ${typeof input}, equal to:\n${JSON.stringify(input, null, 4)}`);
181
+ }
182
+ else if (!input.trim()) {
183
+ return { res: [[""]], msgContent, msgType };
184
+ }
185
+ let content = splitEasy(input);
186
+ let schema = [];
187
+ let stateHeaderRowPresent = false;
188
+ let stateDataColumnRowLengthIsConsistent = true;
189
+ const stateColumnsContainingSameValueEverywhere = [];
190
+ let indexAtWhichEmptyCellsStart = null;
191
+ for (let i = content.length - 1; i >= 0; i--) {
192
+ if (!schema.length) {
193
+ /* istanbul ignore next */
194
+ if (content[i].length !== 1 || content[i][0] !== "") {
195
+ for (let y = 0, len = content[i].length; y < len; y++) {
196
+ schema.push(findType(content[i][y].trim()));
197
+ if (indexAtWhichEmptyCellsStart === null &&
198
+ findType(content[i][y].trim()) === "empty") {
199
+ indexAtWhichEmptyCellsStart = y;
200
+ }
201
+ if (indexAtWhichEmptyCellsStart !== null &&
202
+ findType(content[i][y].trim()) !== "empty") {
203
+ indexAtWhichEmptyCellsStart = null;
204
+ }
205
+ }
206
+ }
70
207
  }
71
- }
72
- } else {
73
- if (i === 0) {
74
- stateHeaderRowPresent = content[i].every(el => findType(el) === "text" || findType(el) === "empty");
75
- }
76
- /* istanbul ignore else */
77
- if (!stateHeaderRowPresent && schema.length !== content[i].length) {
78
- stateDataColumnRowLengthIsConsistent = false;
79
- }
80
- let perRowIndexAtWhichEmptyCellsStart = null;
81
- for (let y = 0, len = content[i].length; y < len; y++) {
82
- /* istanbul ignore else */
83
- if (perRowIndexAtWhichEmptyCellsStart === null && findType(content[i][y].trim()) === "empty") {
84
- perRowIndexAtWhichEmptyCellsStart = y;
208
+ else {
209
+ if (i === 0) {
210
+ stateHeaderRowPresent = content[i].every((el) => findType(el) === "text" || findType(el) === "empty");
211
+ }
212
+ /* istanbul ignore else */
213
+ if (!stateHeaderRowPresent && schema.length !== content[i].length) {
214
+ stateDataColumnRowLengthIsConsistent = false;
215
+ }
216
+ let perRowIndexAtWhichEmptyCellsStart = null;
217
+ for (let y = 0, len = content[i].length; y < len; y++) {
218
+ /* istanbul ignore else */
219
+ if (perRowIndexAtWhichEmptyCellsStart === null &&
220
+ findType(content[i][y].trim()) === "empty") {
221
+ perRowIndexAtWhichEmptyCellsStart = y;
222
+ }
223
+ /* istanbul ignore else */
224
+ if (perRowIndexAtWhichEmptyCellsStart !== null &&
225
+ findType(content[i][y].trim()) !== "empty") {
226
+ perRowIndexAtWhichEmptyCellsStart = null;
227
+ }
228
+ /* istanbul ignore else */
229
+ if (findType(content[i][y].trim()) !== schema[y] &&
230
+ !stateHeaderRowPresent) {
231
+ const toAdd = findType(content[i][y].trim());
232
+ /* istanbul ignore else */
233
+ if (Array.isArray(schema[y])) {
234
+ if (!schema[y].includes(toAdd)) {
235
+ schema[y].push(findType(content[i][y].trim()));
236
+ }
237
+ }
238
+ else if (schema[y] !== toAdd) {
239
+ const temp = schema[y];
240
+ schema[y] = [];
241
+ schema[y].push(temp);
242
+ schema[y].push(toAdd);
243
+ }
244
+ }
245
+ }
246
+ /* istanbul ignore next */
247
+ if (indexAtWhichEmptyCellsStart !== null &&
248
+ perRowIndexAtWhichEmptyCellsStart !== null &&
249
+ perRowIndexAtWhichEmptyCellsStart > indexAtWhichEmptyCellsStart &&
250
+ (!stateHeaderRowPresent || (stateHeaderRowPresent && i !== 0))) {
251
+ indexAtWhichEmptyCellsStart = perRowIndexAtWhichEmptyCellsStart;
252
+ }
85
253
  }
86
- /* istanbul ignore else */
87
- if (perRowIndexAtWhichEmptyCellsStart !== null && findType(content[i][y].trim()) !== "empty") {
88
- perRowIndexAtWhichEmptyCellsStart = null;
254
+ }
255
+ /* istanbul ignore else */
256
+ if (!indexAtWhichEmptyCellsStart) {
257
+ indexAtWhichEmptyCellsStart = schema.length;
258
+ }
259
+ let nonEmptyColsStartAt = 0;
260
+ for (let i = 0, len = schema.length; i < len; i++) {
261
+ if (schema[i] === "empty") {
262
+ nonEmptyColsStartAt = i;
89
263
  }
90
- /* istanbul ignore else */
91
- if (findType(content[i][y].trim()) !== schema[y] && !stateHeaderRowPresent) {
92
- const toAdd = findType(content[i][y].trim());
93
- /* istanbul ignore else */
94
- if (Array.isArray(schema[y])) {
95
- if (!schema[y].includes(toAdd)) {
96
- schema[y].push(findType(content[i][y].trim()));
97
- }
98
- } else if (schema[y] !== toAdd) {
99
- const temp = schema[y];
100
- schema[y] = [];
101
- schema[y].push(temp);
102
- schema[y].push(toAdd);
103
- }
264
+ else {
265
+ break;
104
266
  }
105
- }
106
- /* istanbul ignore next */
107
- if (indexAtWhichEmptyCellsStart !== null && perRowIndexAtWhichEmptyCellsStart !== null && perRowIndexAtWhichEmptyCellsStart > indexAtWhichEmptyCellsStart && (!stateHeaderRowPresent || stateHeaderRowPresent && i !== 0)) {
108
- indexAtWhichEmptyCellsStart = perRowIndexAtWhichEmptyCellsStart;
109
- }
110
267
  }
111
- }
112
- /* istanbul ignore else */
113
- if (!indexAtWhichEmptyCellsStart) {
114
- indexAtWhichEmptyCellsStart = schema.length;
115
- }
116
- let nonEmptyColsStartAt = 0;
117
- for (let i = 0, len = schema.length; i < len; i++) {
118
- if (schema[i] === "empty") {
119
- nonEmptyColsStartAt = i;
120
- } else {
121
- break;
268
+ /* istanbul ignore else */
269
+ if (nonEmptyColsStartAt !== 0) {
270
+ content = content.map((arr) => arr.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart));
271
+ schema = schema.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart);
122
272
  }
123
- }
124
- /* istanbul ignore else */
125
- if (nonEmptyColsStartAt !== 0) {
126
- content = content.map(arr => arr.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart));
127
- schema = schema.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart);
128
- }
129
- const numericSchemaColumns = [];
130
- let balanceColumnIndex;
131
- schema.forEach((colType, i) => {
132
- if (colType === "numeric") {
133
- numericSchemaColumns.push(i);
273
+ const numericSchemaColumns = [];
274
+ let balanceColumnIndex;
275
+ schema.forEach((colType, i) => {
276
+ if (colType === "numeric") {
277
+ numericSchemaColumns.push(i);
278
+ }
279
+ });
280
+ const traverseUpToThisIndexAtTheTop = stateHeaderRowPresent ? 1 : 0;
281
+ if (numericSchemaColumns.length === 1) {
282
+ balanceColumnIndex = numericSchemaColumns[0];
134
283
  }
135
- });
136
- const traverseUpToThisIndexAtTheTop = stateHeaderRowPresent ? 1 : 0;
137
- if (numericSchemaColumns.length === 1) {
138
- balanceColumnIndex = numericSchemaColumns[0];
139
- } else if (numericSchemaColumns.length === 0) {
140
- throw new Error('csv-sort/csvSort(): [THROW_ID_03] Your CSV file does not contain numeric-only columns and computer was not able to detect the "Balance" column!');
141
- } else {
142
- let potentialBalanceColumnIndexesList = Array.from(numericSchemaColumns);
143
- const deleteFromPotentialBalanceColumnIndexesList = [];
144
- for (let i = 0, len = potentialBalanceColumnIndexesList.length; i < len; i++) {
145
- const suspectedBalanceColumnsIndexNumber = potentialBalanceColumnIndexesList[i];
146
- let previousValue;
147
- let lookForTwoEqualAndConsecutive = true;
148
- let firstValue;
149
- let lookForAllTheSame = true;
150
- for (let rowNum = traverseUpToThisIndexAtTheTop, len2 = content.length; rowNum < len2; rowNum++) {
151
- /* istanbul ignore else */
152
- if (lookForTwoEqualAndConsecutive) {
153
- if (previousValue == null) {
154
- previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
155
- } else if (previousValue === content[rowNum][suspectedBalanceColumnsIndexNumber]) {
156
- deleteFromPotentialBalanceColumnIndexesList.push(suspectedBalanceColumnsIndexNumber);
157
- lookForTwoEqualAndConsecutive = false;
158
- } else {
159
- previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
160
- }
284
+ else if (numericSchemaColumns.length === 0) {
285
+ throw new Error('csv-sort/csvSort(): [THROW_ID_03] Your CSV file does not contain numeric-only columns and computer was not able to detect the "Balance" column!');
286
+ }
287
+ else {
288
+ let potentialBalanceColumnIndexesList = Array.from(numericSchemaColumns);
289
+ const deleteFromPotentialBalanceColumnIndexesList = [];
290
+ for (let i = 0, len = potentialBalanceColumnIndexesList.length; i < len; i++) {
291
+ const suspectedBalanceColumnsIndexNumber = potentialBalanceColumnIndexesList[i];
292
+ let previousValue;
293
+ let lookForTwoEqualAndConsecutive = true;
294
+ let firstValue;
295
+ let lookForAllTheSame = true;
296
+ for (let rowNum = traverseUpToThisIndexAtTheTop, len2 = content.length; rowNum < len2; rowNum++) {
297
+ /* istanbul ignore else */
298
+ if (lookForTwoEqualAndConsecutive) {
299
+ if (previousValue == null) {
300
+ previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
301
+ }
302
+ else if (previousValue ===
303
+ content[rowNum][suspectedBalanceColumnsIndexNumber]) {
304
+ deleteFromPotentialBalanceColumnIndexesList.push(suspectedBalanceColumnsIndexNumber);
305
+ lookForTwoEqualAndConsecutive = false;
306
+ }
307
+ else {
308
+ previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
309
+ }
310
+ }
311
+ /* istanbul ignore else */
312
+ if (lookForAllTheSame) {
313
+ if (firstValue == null) {
314
+ firstValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
315
+ }
316
+ else if (content[rowNum][suspectedBalanceColumnsIndexNumber] !== firstValue) {
317
+ lookForAllTheSame = false;
318
+ }
319
+ }
320
+ if (!lookForTwoEqualAndConsecutive) {
321
+ break;
322
+ }
323
+ }
324
+ /* istanbul ignore else */
325
+ if (lookForAllTheSame) {
326
+ stateColumnsContainingSameValueEverywhere.push(suspectedBalanceColumnsIndexNumber);
327
+ }
161
328
  }
329
+ potentialBalanceColumnIndexesList = pull(potentialBalanceColumnIndexesList, ...deleteFromPotentialBalanceColumnIndexesList);
162
330
  /* istanbul ignore else */
163
- if (lookForAllTheSame) {
164
- if (firstValue == null) {
165
- firstValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
166
- } else if (content[rowNum][suspectedBalanceColumnsIndexNumber] !== firstValue) {
167
- lookForAllTheSame = false;
168
- }
331
+ if (potentialBalanceColumnIndexesList.length === 1) {
332
+ balanceColumnIndex = potentialBalanceColumnIndexesList[0];
169
333
  }
170
- if (!lookForTwoEqualAndConsecutive) {
171
- break;
334
+ else if (potentialBalanceColumnIndexesList.length === 0) {
335
+ throw new Error('csv-sort/csvSort(): [THROW_ID_04] The computer can\'t find the "Balance" column! It saw some numeric-only columns, but they all seem to have certain rows with the same values as rows right below/above them!');
172
336
  }
173
- }
174
- /* istanbul ignore else */
175
- if (lookForAllTheSame) {
176
- stateColumnsContainingSameValueEverywhere.push(suspectedBalanceColumnsIndexNumber);
177
- }
337
+ else ;
178
338
  }
179
- potentialBalanceColumnIndexesList = pull(potentialBalanceColumnIndexesList, ...deleteFromPotentialBalanceColumnIndexesList);
180
- /* istanbul ignore else */
181
- if (potentialBalanceColumnIndexesList.length === 1) {
182
- balanceColumnIndex = potentialBalanceColumnIndexesList[0];
183
- } else if (potentialBalanceColumnIndexesList.length === 0) {
184
- throw new Error('csv-sort/csvSort(): [THROW_ID_04] The computer can\'t find the "Balance" column! It saw some numeric-only columns, but they all seem to have certain rows with the same values as rows right below/above them!');
185
- } else ;
186
- }
187
- if (!balanceColumnIndex) {
188
- throw new Error("csv-sort/csvSort(): [THROW_ID_05] Sadly computer couldn't find its way in this CSV and had to stop working on it.");
189
- }
190
- const potentialCreditDebitColumns = pull(Array.from(schema.reduce((result, el, index) => {
191
- if (typeof el === "string" && el === "numeric" || Array.isArray(el) && el.includes("numeric")) {
192
- result.push(index);
339
+ if (!balanceColumnIndex) {
340
+ throw new Error("csv-sort/csvSort(): [THROW_ID_05] Sadly computer couldn't find its way in this CSV and had to stop working on it.");
193
341
  }
194
- return result;
195
- }, [])), balanceColumnIndex, ...stateColumnsContainingSameValueEverywhere);
196
- const resContent = [];
197
- resContent.push(content[content.length - 1].slice(0, indexAtWhichEmptyCellsStart));
198
- const usedUpRows = [];
199
- const bottom = stateHeaderRowPresent ? 1 : 0;
200
- for (let y = content.length - 2; y >= bottom; y--) {
201
- for (let suspectedRowsIndex = content.length - 2; suspectedRowsIndex >= bottom; suspectedRowsIndex--) {
202
- if (!usedUpRows.includes(suspectedRowsIndex)) {
203
- let thisRowIsDone = false;
204
- for (let suspectedColIndex = 0, len = potentialCreditDebitColumns.length; suspectedColIndex < len; suspectedColIndex++) {
205
- let diffVal = null;
206
- if (content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
207
- diffVal = currency(content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]]);
208
- }
209
- let totalVal = null;
210
- /* istanbul ignore else */
211
- if (content[suspectedRowsIndex][balanceColumnIndex] !== "") {
212
- totalVal = currency(content[suspectedRowsIndex][balanceColumnIndex]);
213
- }
214
- let topmostResContentBalance = null;
215
- /* istanbul ignore else */
216
- if (resContent[0][balanceColumnIndex] !== "") {
217
- topmostResContentBalance = currency(resContent[0][balanceColumnIndex]).format();
218
- }
219
- let currentRowsDiffVal = null;
220
- /* istanbul ignore else */
221
- if (resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
222
- currentRowsDiffVal = currency(resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]]).format();
223
- }
224
- let lastResContentRowsBalance = null;
225
- /* istanbul ignore else */
226
- if (resContent[resContent.length - 1][balanceColumnIndex] !== "") {
227
- lastResContentRowsBalance = currency(resContent[resContent.length - 1][balanceColumnIndex]);
228
- }
229
- /* istanbul ignore else */
230
- if (diffVal && totalVal.add(diffVal).format() === topmostResContentBalance) {
231
- resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
232
- usedUpRows.push(suspectedRowsIndex);
233
- thisRowIsDone = true;
234
- break;
235
- } else if (diffVal && totalVal.subtract(diffVal).format() === topmostResContentBalance) {
236
- resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
237
- usedUpRows.push(suspectedRowsIndex);
238
- thisRowIsDone = true;
239
- break;
240
- } else if (currentRowsDiffVal && lastResContentRowsBalance.add(currentRowsDiffVal).format() === totalVal.format()) {
241
- resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
242
- usedUpRows.push(suspectedRowsIndex);
243
- thisRowIsDone = true;
244
- break;
245
- } else if (currentRowsDiffVal && lastResContentRowsBalance.subtract(currentRowsDiffVal).format() === totalVal.format()) {
246
- resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
247
- usedUpRows.push(suspectedRowsIndex);
248
- thisRowIsDone = true;
249
- break;
250
- }
342
+ const potentialCreditDebitColumns = pull(Array.from(schema.reduce((result, el, index) => {
343
+ if ((typeof el === "string" && el === "numeric") ||
344
+ (Array.isArray(el) && el.includes("numeric"))) {
345
+ result.push(index);
251
346
  }
252
- /* istanbul ignore else */
253
- if (thisRowIsDone) {
254
- thisRowIsDone = false;
255
- break;
347
+ return result;
348
+ }, [])), balanceColumnIndex, ...stateColumnsContainingSameValueEverywhere);
349
+ const resContent = [];
350
+ resContent.push(content[content.length - 1].slice(0, indexAtWhichEmptyCellsStart));
351
+ const usedUpRows = [];
352
+ const bottom = stateHeaderRowPresent ? 1 : 0;
353
+ for (let y = content.length - 2; y >= bottom; y--) {
354
+ for (let suspectedRowsIndex = content.length - 2; suspectedRowsIndex >= bottom; suspectedRowsIndex--) {
355
+ if (!usedUpRows.includes(suspectedRowsIndex)) {
356
+ let thisRowIsDone = false;
357
+ for (let suspectedColIndex = 0, len = potentialCreditDebitColumns.length; suspectedColIndex < len; suspectedColIndex++) {
358
+ let diffVal = null;
359
+ if (content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
360
+ diffVal = currency(content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]]);
361
+ }
362
+ let totalVal = null;
363
+ /* istanbul ignore else */
364
+ if (content[suspectedRowsIndex][balanceColumnIndex] !== "") {
365
+ totalVal = currency(content[suspectedRowsIndex][balanceColumnIndex]);
366
+ }
367
+ let topmostResContentBalance = null;
368
+ /* istanbul ignore else */
369
+ if (resContent[0][balanceColumnIndex] !== "") {
370
+ topmostResContentBalance = currency(resContent[0][balanceColumnIndex]).format();
371
+ }
372
+ let currentRowsDiffVal = null;
373
+ /* istanbul ignore else */
374
+ if (resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
375
+ currentRowsDiffVal = currency(resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]]).format();
376
+ }
377
+ let lastResContentRowsBalance = null;
378
+ /* istanbul ignore else */
379
+ if (resContent[resContent.length - 1][balanceColumnIndex] !== "") {
380
+ lastResContentRowsBalance = currency(resContent[resContent.length - 1][balanceColumnIndex]);
381
+ }
382
+ /* istanbul ignore else */
383
+ if (diffVal &&
384
+ totalVal.add(diffVal).format() ===
385
+ topmostResContentBalance) {
386
+ resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
387
+ usedUpRows.push(suspectedRowsIndex);
388
+ thisRowIsDone = true;
389
+ break;
390
+ }
391
+ else if (diffVal &&
392
+ totalVal.subtract(diffVal).format() ===
393
+ topmostResContentBalance) {
394
+ resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
395
+ usedUpRows.push(suspectedRowsIndex);
396
+ thisRowIsDone = true;
397
+ break;
398
+ }
399
+ else if (currentRowsDiffVal &&
400
+ lastResContentRowsBalance
401
+ .add(currentRowsDiffVal)
402
+ .format() === totalVal.format()) {
403
+ resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
404
+ usedUpRows.push(suspectedRowsIndex);
405
+ thisRowIsDone = true;
406
+ break;
407
+ }
408
+ else if (currentRowsDiffVal &&
409
+ lastResContentRowsBalance
410
+ .subtract(currentRowsDiffVal)
411
+ .format() === totalVal.format()) {
412
+ resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
413
+ usedUpRows.push(suspectedRowsIndex);
414
+ thisRowIsDone = true;
415
+ break;
416
+ }
417
+ }
418
+ /* istanbul ignore else */
419
+ if (thisRowIsDone) {
420
+ thisRowIsDone = false;
421
+ break;
422
+ }
423
+ }
424
+ }
425
+ }
426
+ /* istanbul ignore else */
427
+ if (stateHeaderRowPresent) {
428
+ if (stateDataColumnRowLengthIsConsistent &&
429
+ content[0].length > schema.length) {
430
+ content[0].length = schema.length;
256
431
  }
257
- }
432
+ resContent.unshift(content[0].slice(0, indexAtWhichEmptyCellsStart));
258
433
  }
259
- }
260
- /* istanbul ignore else */
261
- if (stateHeaderRowPresent) {
262
- if (stateDataColumnRowLengthIsConsistent && content[0].length > schema.length) {
263
- content[0].length = schema.length;
434
+ /* istanbul ignore else */
435
+ if (content.length - (stateHeaderRowPresent ? 2 : 1) !== usedUpRows.length) {
436
+ msgContent = "Not all rows were recognised!";
437
+ msgType = "alert";
264
438
  }
265
- resContent.unshift(content[0].slice(0, indexAtWhichEmptyCellsStart));
266
- }
267
- /* istanbul ignore else */
268
- if (content.length - (stateHeaderRowPresent ? 2 : 1) !== usedUpRows.length) {
269
- msgContent = "Not all rows were recognised!";
270
- msgType = "alert";
271
- }
272
- return {
273
- res: resContent,
274
- msgContent,
275
- msgType
276
- };
439
+ return {
440
+ res: resContent,
441
+ msgContent,
442
+ msgType,
443
+ };
277
444
  }
278
445
 
279
446
  export { sort };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @name csv-sort
3
3
  * @fileoverview Sorts double-entry bookkeeping CSV coming from internet banking
4
- * @version 6.0.5
4
+ * @version 6.0.6
5
5
  * @author Roy Revelt, Codsen Ltd
6
6
  * @license MIT
7
7
  * {@link https://codsen.com/os/csv-sort/}
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * @name ranges-sort
13
13
  * @fileoverview Sort string index ranges
14
- * @version 5.0.5
14
+ * @version 5.0.6
15
15
  * @author Roy Revelt, Codsen Ltd
16
16
  * @license MIT
17
17
  * {@link https://codsen.com/os/ranges-sort/}
@@ -19,7 +19,7 @@
19
19
  /**
20
20
  * @name ranges-merge
21
21
  * @fileoverview Merge and sort string index ranges
22
- * @version 8.0.5
22
+ * @version 8.0.6
23
23
  * @author Roy Revelt, Codsen Ltd
24
24
  * @license MIT
25
25
  * {@link https://codsen.com/os/ranges-merge/}
@@ -27,7 +27,7 @@
27
27
  /**
28
28
  * @name ranges-apply
29
29
  * @fileoverview Take an array of string index ranges, delete/replace the string according to them
30
- * @version 6.0.5
30
+ * @version 6.0.6
31
31
  * @author Roy Revelt, Codsen Ltd
32
32
  * @license MIT
33
33
  * {@link https://codsen.com/os/ranges-apply/}
@@ -35,7 +35,7 @@
35
35
  /**
36
36
  * @name string-collapse-leading-whitespace
37
37
  * @fileoverview Collapse the leading and trailing whitespace of a string
38
- * @version 6.0.5
38
+ * @version 6.0.6
39
39
  * @author Roy Revelt, Codsen Ltd
40
40
  * @license MIT
41
41
  * {@link https://codsen.com/os/string-collapse-leading-whitespace/}
@@ -44,7 +44,7 @@ function d(e,r=1){function t(e){return Array.from(e).reverse().join("")}function
44
44
  /**
45
45
  * @name ranges-push
46
46
  * @fileoverview Gather string index ranges
47
- * @version 6.0.5
47
+ * @version 6.0.6
48
48
  * @author Roy Revelt, Codsen Ltd
49
49
  * @license MIT
50
50
  * {@link https://codsen.com/os/ranges-push/}
@@ -52,7 +52,7 @@ function d(e,r=1){function t(e){return Array.from(e).reverse().join("")}function
52
52
  /**
53
53
  * @name string-remove-thousand-separators
54
54
  * @fileoverview Detects and removes thousand separators (dot/comma/quote/space) from string-type digits
55
- * @version 6.0.5
55
+ * @version 6.0.6
56
56
  * @author Roy Revelt, Codsen Ltd
57
57
  * @license MIT
58
58
  * {@link https://codsen.com/os/string-remove-thousand-separators/}
@@ -60,7 +60,7 @@ function d(e,r=1){function t(e){return Array.from(e).reverse().join("")}function
60
60
  /**
61
61
  * @name csv-split-easy
62
62
  * @fileoverview Splits the CSV string into array of arrays, each representing a row of columns
63
- * @version 6.0.5
63
+ * @version 6.0.6
64
64
  * @author Roy Revelt, Codsen Ltd
65
65
  * @license MIT
66
66
  * {@link https://codsen.com/os/csv-split-easy/}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "csv-sort",
3
- "version": "6.0.5",
3
+ "version": "6.0.6",
4
4
  "description": "Sorts double-entry bookkeeping CSV coming from internet banking",
5
5
  "keywords": [
6
6
  "accountancy",
@@ -39,13 +39,12 @@
39
39
  "types": "types/index.d.ts",
40
40
  "scripts": {
41
41
  "build": "rollup -c",
42
- "ci_test": "npm run build && npm run format && tap --no-only --reporter=silent --output-file=testStats.md && npm run clean_cov",
43
- "clean_cov": "../../scripts/leaveCoverageTotalOnly.js",
42
+ "build:esbuild": "node '../../scripts/esbuild.js'",
43
+ "build:esbuild:dev": "cross-env MODE=dev node '../../scripts/esbuild.js'",
44
+ "ci_test": "npm run build && npm run format && tap --no-only --reporter=silent",
44
45
  "clean_types": "../../scripts/cleanTypes.js",
45
46
  "dev": "rollup -c --dev",
46
47
  "devunittest": "npm run dev && tap --only -R 'base'",
47
- "esbuild": "node '../../scripts/esbuild.js'",
48
- "esbuild_dev": "cross-env MODE=dev node '../../scripts/esbuild.js'",
49
48
  "format": "npm run lect && npm run prettier && npm run lint",
50
49
  "lect": "lect",
51
50
  "lint": "../../node_modules/eslint/bin/eslint.js . --ext .js --ext .ts --fix --config \"../../.eslintrc.json\" --quiet",
@@ -54,17 +53,14 @@
54
53
  "republish": "npm publish || :",
55
54
  "tap": "tap",
56
55
  "pretest": "npm run build",
57
- "test": "npm run lint && npm run unittest && npm run test:examples && npm run clean_cov && npm run format",
56
+ "test": "npm run test:ci && npm run perf",
57
+ "test:ci": "npm run unittest && npm run test:examples && npm run format",
58
58
  "test:examples": "../../scripts/test-examples.js && npm run lect && npm run prettier",
59
59
  "tsc": "tsc",
60
- "unittest": "tap --no-only --output-file=testStats.md --reporter=terse && tsc -p tsconfig.json --noEmit && npm run clean_cov && npm run perf"
60
+ "unittest": "tap --no-only --reporter=terse && tsc -p tsconfig.json --noEmit"
61
61
  },
62
62
  "tap": {
63
63
  "check-coverage": false,
64
- "coverage-report": [
65
- "json-summary",
66
- "text"
67
- ],
68
64
  "node-arg": [
69
65
  "--no-warnings",
70
66
  "--experimental-loader",
@@ -87,8 +83,8 @@
87
83
  }
88
84
  },
89
85
  "dependencies": {
90
- "@babel/runtime": "^7.16.0",
91
- "csv-split-easy": "^6.0.5",
86
+ "@babel/runtime": "^7.16.3",
87
+ "csv-split-easy": "^6.0.6",
92
88
  "currency.js": "^2.0.4",
93
89
  "lodash.pull": "^4.1.0"
94
90
  },
@@ -101,8 +97,8 @@
101
97
  "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0",
102
98
  "@babel/plugin-proposal-object-rest-spread": "^7.16.0",
103
99
  "@babel/plugin-proposal-optional-chaining": "^7.16.0",
104
- "@babel/plugin-transform-runtime": "^7.16.0",
105
- "@babel/preset-env": "^7.16.0",
100
+ "@babel/plugin-transform-runtime": "^7.16.4",
101
+ "@babel/preset-env": "^7.16.4",
106
102
  "@babel/preset-typescript": "^7.16.0",
107
103
  "@babel/register": "^7.16.0",
108
104
  "@istanbuljs/esm-loader-hook": "^0.1.2",
@@ -113,25 +109,25 @@
113
109
  "@rollup/plugin-strip": "^2.1.0",
114
110
  "@rollup/plugin-typescript": "^8.3.0",
115
111
  "@types/lodash.pull": "^4.1.6",
116
- "@types/node": "^16.11.6",
112
+ "@types/node": "^16.11.9",
117
113
  "@types/tap": "^15.0.5",
118
- "@typescript-eslint/eslint-plugin": "^5.3.0",
119
- "@typescript-eslint/parser": "^5.3.0",
114
+ "@typescript-eslint/eslint-plugin": "^5.4.0",
115
+ "@typescript-eslint/parser": "^5.4.0",
120
116
  "core-js": "^3.19.1",
121
117
  "cross-env": "^7.0.3",
122
- "eslint": "^8.2.0",
123
- "lect": "^0.18.5",
124
- "rollup": "^2.59.0",
118
+ "eslint": "^8.3.0",
119
+ "lect": "^0.18.6",
120
+ "rollup": "^2.60.0",
125
121
  "rollup-plugin-ascii": "^0.0.3",
126
122
  "rollup-plugin-banner": "^0.2.1",
127
123
  "rollup-plugin-cleanup": "^3.2.1",
128
124
  "rollup-plugin-dts": "^4.0.1",
129
125
  "rollup-plugin-terser": "^7.0.2",
130
- "tap": "^15.0.10",
126
+ "tap": "^15.1.2",
131
127
  "tslib": "^2.3.1",
132
- "typescript": "^4.4.4"
128
+ "typescript": "^4.5.2"
133
129
  },
134
130
  "engines": {
135
- "node": ">=12"
131
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
136
132
  }
137
133
  }