1 | "use strict";
|
2 |
|
3 | const O = Object
|
4 | , { first, strlen } = require ('printable-characters')
|
5 | , limit = (s, n) => (first (s, n - 1) + '…')
|
6 |
|
7 | const asColumns = (rows, cfg_) => {
|
8 |
|
9 | const
|
10 |
|
11 | zip = (arrs, f) => arrs.reduce ((a, b) => b.map ((b, i) => [...a[i] || [], b]), []).map (args => f (...args)),
|
12 |
|
13 |
|
14 |
|
15 | cells = rows.map (r => r.map (c => c.replace (/\n/g, '\\n'))),
|
16 |
|
17 |
|
18 |
|
19 | cellWidths = cells.map (r => r.map (strlen)),
|
20 | maxWidths = zip (cellWidths, Math.max),
|
21 |
|
22 |
|
23 |
|
24 | cfg = O.assign ({
|
25 | delimiter: ' ',
|
26 | minColumnWidths: maxWidths.map (x => 0),
|
27 | maxTotalWidth: 0 }, cfg_),
|
28 |
|
29 | delimiterLength = strlen (cfg.delimiter),
|
30 |
|
31 |
|
32 |
|
33 | totalWidth = maxWidths.reduce ((a, b) => a + b, 0),
|
34 | relativeWidths = maxWidths.map (w => w / totalWidth),
|
35 | maxTotalWidth = cfg.maxTotalWidth - (delimiterLength * (maxWidths.length - 1)),
|
36 | excessWidth = Math.max (0, totalWidth - maxTotalWidth),
|
37 | computedWidths = zip ([cfg.minColumnWidths, maxWidths, relativeWidths],
|
38 | (min, max, relative) => Math.max (min, Math.floor (max - excessWidth * relative))),
|
39 |
|
40 |
|
41 |
|
42 | restCellWidths = cellWidths.map (widths => zip ([computedWidths, widths], (a, b) => a - b))
|
43 |
|
44 |
|
45 |
|
46 | return zip ([cells, restCellWidths], (a, b) =>
|
47 | zip ([a, b], (str, w) => (w >= 0)
|
48 | ? (cfg.right ? (' '.repeat (w) + str) : (str + ' '.repeat (w)))
|
49 | : (limit (str, strlen (str) + w))).join (cfg.delimiter))
|
50 | }
|
51 |
|
52 | const asTable = cfg => O.assign (arr => {
|
53 |
|
54 |
|
55 |
|
56 | if (arr[0] && Array.isArray (arr[0]))
|
57 | return asColumns (
|
58 | arr.map (
|
59 | r => r.map (
|
60 | (c, i) => (c === undefined) ? '' : cfg.print(c, i)
|
61 | )
|
62 | ),
|
63 | cfg
|
64 | ).join ('\n')
|
65 |
|
66 |
|
67 |
|
68 | const colNames = [...new Set ([].concat (...arr.map (O.keys)))],
|
69 | columns = [
|
70 | colNames.map (cfg.title),
|
71 | ...arr.map (
|
72 | o => colNames.map (
|
73 | key => (o[key] === undefined) ? '' : cfg.print(o[key], key)
|
74 | )
|
75 | )
|
76 | ],
|
77 | lines = asColumns (columns, cfg)
|
78 |
|
79 | return [lines[0], cfg.dash.repeat (strlen (lines[0])), ...lines.slice (1)].join ('\n')
|
80 |
|
81 | }, cfg, {
|
82 |
|
83 | configure: newConfig => asTable (O.assign ({}, cfg, newConfig)),
|
84 | })
|
85 |
|
86 | module.exports = asTable ({
|
87 |
|
88 | maxTotalWidth: Number.MAX_SAFE_INTEGER,
|
89 | print: String,
|
90 | title: String,
|
91 | dash: '-',
|
92 | right: false
|
93 | })
|