-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathindex.js
85 lines (68 loc) · 2.21 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import execall from 'execall';
import splitAt from 'split-at';
import escapeStringRegexp from 'escape-string-regexp';
/*
Algorithm:
Find separators that are on the same index on each line, remove consecutive ones, then split on those indexes. It's important to check each line as you don't want to split in the middle of a column row just because it contains the separator.
*/
const countSeparators = (lines, separator = ' ') => {
const counts = [];
const separatorRegex = new RegExp(escapeStringRegexp(separator), 'g');
const headerLength = (lines[0] || '').length;
for (let line of lines) {
// Ensure lines are as long as the header
const padAmount = Math.ceil(Math.max(headerLength - line.length, 0) / separator.length);
line += separator.repeat(padAmount);
for (const {index: column} of execall(separatorRegex, line)) {
counts[column] = typeof counts[column] === 'number' ? counts[column] + 1 : 1;
}
}
return counts;
};
const getSplits = (lines, separator) => {
const counts = countSeparators(lines, separator);
const splits = [];
let consecutive = false;
for (const [index, count] of counts.entries()) {
if (count !== lines.length) { // eslint-disable-line no-negated-condition
consecutive = false;
} else {
if (index !== 0 && !consecutive) {
splits.push(index);
}
consecutive = true;
}
}
return splits;
};
export default function parseColumns(input, options = {}) {
const lines = input.replace(/^\s*\n|\s+$/g, '').split('\n');
let splits = getSplits(lines, options.separator);
const {transform} = options;
const rows = [];
let items;
let {headers} = options;
if (!headers) {
headers = [];
items = splitAt(lines[0], splits, {remove: true});
for (let [index, item] of items.entries()) {
item = item.trim();
if (item) {
headers.push(item);
} else {
splits[index - 1] = null;
}
}
splits = splits.filter(Boolean);
}
for (const [index, line] of lines.slice(1).entries()) {
items = splitAt(line, splits, {remove: true});
const row = {};
for (const [index2, header] of headers.entries()) {
const item = (items[index2] || '').trim();
row[header] = transform ? transform(item, header, index2, index) : item;
}
rows.push(row);
}
return rows;
}