1 | "use strict"; |
||
2 | |||
3 | const |
||
4 | |||
5 | { first, strlen } = require ('printable-characters'), // handles ANSI codes and invisible characters |
||
6 | |||
7 | limit = (s, n) => (first (s, n - 1) + '…'), |
||
8 | |||
9 | max = (a, b) => (a === undefined) ? b : ((b === undefined) ? a : Math.max (a, b)), |
||
10 | |||
11 | arrmax = (a, b) => a.length > b.length ? a : b, |
||
0 ignored issues
–
show
Unused Code
introduced
by
![]() |
|||
12 | |||
13 | asColumns = (rows, cfg_) => { |
||
14 | |||
15 | const |
||
16 | |||
17 | zip = (arrs, f) => arrs.reduce ((a, b) => b.map ((b, i) => [...a[i] || [], b]), []) |
||
18 | .map (args => f (...args)), |
||
19 | |||
20 | //dasd = zip ([[1,2,3], [4,5,6], [7,8,9], [10,11,12]], (...args) => console.log (args)), |
||
21 | |||
22 | /* Convert cell data to string (converting multiline text to singleline) */ |
||
23 | |||
24 | cells = rows.map (r => r.map (c => (c === undefined) ? '' : cfg_.print (c).replace (/\n/g, '\\n'))), |
||
25 | |||
26 | /* Compute column widths (per row) and max widths (per column) */ |
||
27 | |||
28 | cellWidths = cells.map (r => r.map (strlen)), |
||
29 | maxWidths = zip (cellWidths, max), |
||
30 | |||
31 | /* Default config */ |
||
32 | |||
33 | cfg = Object.assign ({ |
||
34 | delimiter: ' ', |
||
35 | minColumnWidths: maxWidths.map (x => 0), |
||
36 | maxTotalWidth: 0 }, cfg_), |
||
37 | |||
38 | delimiterLength = strlen (cfg.delimiter), |
||
39 | |||
40 | /* Project desired column widths, taking maxTotalWidth and minColumnWidths in account. */ |
||
41 | |||
42 | totalWidth = maxWidths.reduce ((a, b) => a + b, 0), |
||
43 | relativeWidths = maxWidths.map (w => w / totalWidth), |
||
44 | maxTotalWidth = cfg.maxTotalWidth - (delimiterLength * (maxWidths.length - 1)), |
||
45 | excessWidth = Math.max (0, totalWidth - maxTotalWidth), |
||
46 | computedWidths = zip ([cfg.minColumnWidths, maxWidths, relativeWidths], |
||
47 | (min, max, relative) => Math.max (min, Math.floor (max - excessWidth * relative))), |
||
48 | |||
49 | /* This is how many symbols we should pad or cut (per column). */ |
||
50 | |||
51 | restCellWidths = cellWidths.map (widths => zip ([computedWidths, widths], (a, b) => a - b)) |
||
52 | |||
53 | /* Perform final composition. */ |
||
54 | |||
55 | return zip ([cells, restCellWidths], (a, b) => |
||
56 | zip ([a, b], (str, w) => (w >= 0) |
||
57 | ? (str + ' '.repeat (w)) |
||
58 | : (limit (str, strlen (str) + w))).join (cfg.delimiter)) |
||
59 | }, |
||
60 | |||
61 | asTable = cfg => Object.assign (arr => { |
||
62 | |||
63 | /* Print arrays */ |
||
64 | |||
65 | if (arr[0] && Array.isArray (arr[0])) |
||
66 | return asColumns (arr, cfg).join ('\n') |
||
67 | |||
68 | /* Print objects */ |
||
69 | |||
70 | const colNames = [...new Set ([].concat (...arr.map (Object.keys)))], |
||
71 | columns = [colNames.map (cfg.title), ...arr.map (o => colNames.map (key => o[key]))], |
||
72 | lines = asColumns (columns, cfg) |
||
73 | |||
74 | return [lines[0], cfg.dash.repeat (strlen (lines[0])), ...lines.slice (1)].join ('\n') |
||
75 | |||
76 | }, cfg, { |
||
77 | |||
78 | configure: newConfig => asTable (Object.assign ({}, cfg, newConfig)), |
||
79 | }) |
||
80 | |||
81 | module.exports = asTable ({ |
||
82 | |||
83 | maxTotalWidth: Number.MAX_SAFE_INTEGER, |
||
84 | print: String, |
||
85 | title: String, |
||
86 | dash: '-' |
||
87 | }) |
||
88 | |||
89 | |||
90 |