Issues (12)

as-table.js (1 issue)

1
"use strict";
2
3
const O = Object
4
    , { first, strlen } = require ('printable-characters') // handles ANSI codes and invisible characters
5
    , limit = (s, n) => (first (s, n - 1) + '…')
6
7 View Code Duplication
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
    /*  Convert cell data to string (converting multiline text to singleline) */
14
15
        cells           = rows.map (r => r.map (c => (c === undefined) ? '' : cfg_.print (c).replace (/\n/g, '\\n'))),
16
17
    /*  Compute column widths (per row) and max widths (per column)     */
18
19
        cellWidths      = cells.map (r => r.map (strlen)),
20
        maxWidths       = zip (cellWidths, Math.max),
21
22
    /*  Default config     */
23
24
        cfg             = O.assign ({
25
                            delimiter: '  ',
26
                            minColumnWidths: maxWidths.map (x => 0),
0 ignored issues
show
The parameter x is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
27
                            maxTotalWidth: 0 }, cfg_),
28
29
        delimiterLength = strlen (cfg.delimiter),
30
31
    /*  Project desired column widths, taking maxTotalWidth and minColumnWidths in account.     */
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
    /*  This is how many symbols we should pad or cut (per column).  */
41
42
        restCellWidths  = cellWidths.map (widths => zip ([computedWidths, widths], (a, b) => a - b))
43
44
    /*  Perform final composition.   */
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
/*  Print arrays  */
55
56
    if (arr[0] && Array.isArray (arr[0]))
57
        return asColumns (arr, cfg).join ('\n')
58
59
/*  Print objects   */
60
61
    const colNames        = [...new Set ([].concat (...arr.map (O.keys)))],
62
          columns         = [colNames.map (cfg.title), ...arr.map (o => colNames.map (key => o[key]))],
63
          lines           = asColumns (columns, cfg)
64
65
    return [lines[0], cfg.dash.repeat (strlen (lines[0])), ...lines.slice (1)].join ('\n')
66
67
}, cfg, {
68
69
    configure: newConfig => asTable (O.assign ({}, cfg, newConfig)),
70
})
71
72
module.exports = asTable ({
73
74
    maxTotalWidth: Number.MAX_SAFE_INTEGER,
75
    print: String,
76
    title: String,
77
    dash: '-',
78
    right: false
79
})