node package manager

table

Formats data into a string table.

Table

Produces a string that represents array data in a text table.

  • Works with strings containing fullwidth characters.
  • Works with strings containing ANSI escape codes.
  • Configurable border characters.
  • Configurable content alignment per column.
  • Configurable content padding per column.
  • Configurable column width.
  • Text wrapping.

Table data is described using an array (rows) of array (cells).

import {
  table
} from 'table';
 
// Using commonjs? 
// const {table} = require('table'); 
 
let data,
    output;
 
data = [
    ['0A', '0B', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C']
];
 
/**
 * @typedef {string} table~cell
 */
 
/**
 * @typedef {table~cell[]} table~row
 */
 
/**
 * @typedef {Object} table~columns
 * @property {string} alignment Cell content alignment (enum: left, center, right) (default: left).
 * @property {number} width Column width (default: auto).
 * @property {number} truncate Number of characters are which the content will be truncated (default: Infinity).
 * @property {number} paddingLeft Cell content padding width left (default: 1).
 * @property {number} paddingRight Cell content padding width right (default: 1).
 */
 
/**
 * @typedef {Object} table~border
 * @property {string} topBody 
 * @property {string} topJoin 
 * @property {string} topLeft 
 * @property {string} topRight 
 * @property {string} bottomBody 
 * @property {string} bottomJoin 
 * @property {string} bottomLeft 
 * @property {string} bottomRight 
 * @property {string} bodyLeft 
 * @property {string} bodyRight 
 * @property {string} bodyJoin 
 * @property {string} joinBody 
 * @property {string} joinLeft 
 * @property {string} joinRight 
 * @property {string} joinJoin 
 */
 
/**
 * Used to dynamically tell table whether to draw a line separating rows or not.
 * The default behavior is to always return true.
 *
 * @typedef {function} drawJoin 
 * @param {number} index 
 * @param {number} size 
 * @return {boolean}
 */
 
/**
 * @typedef {Object} table~config
 * @property {table~border} border
 * @property {table~columns[]} columns Column specific configuration.
 * @property {table~columns} columnDefault Default values for all columns. Column specific settings overwrite the default values.
 * @property {table~drawJoin} drawHorizontalLine
 */
 
/**
 * Generates a text table.
 *
 * @param {table~row[]} rows
 * @param {table~config} config
 * @return {String}
 */
output = table(data);
 
console.log(output);
╔════╤════╤════╗
║ 0A │ 0B │ 0C ║
╟────┼────┼────╢
║ 1A │ 1B │ 1C ║
╟────┼────┼────╢
║ 2A │ 2B │ 2C ║
╚════╧════╧════╝

{string} config.columns[{number}].alignment property controls content horizontal alignment within a cell.

Valid values are: "left", "right" and "center".

let config,
    data,
    output;
 
data = [
    ['0A', '0B', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C']
];
 
config = {
    columns: {
        0: {
            alignment: 'left',
            minWidth: 10
        },
        1: {
            alignment: 'center',
            minWidth: 10
        },
        2: {
            alignment: 'right',
            minWidth: 10
        }
    }
};
 
output = table(data, config);
 
console.log(output);
╔════════════╤════════════╤════════════╗
║ 0A         │     0B     │         0C ║
╟────────────┼────────────┼────────────╢
║ 1A         │     1B     │         1C ║
╟────────────┼────────────┼────────────╢
║ 2A         │     2B     │         2C ║
╚════════════╧════════════╧════════════╝

{number} config.columns[{number}].width property restricts column width to a fixed width.

let data,
    output,
    options;
 
data = [
    ['0A', '0B', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C']
];
 
options = {
    columns: {
        1: {
            width: 10
        }
    }
};
 
output = table(data, options);
 
console.log(output);
╔════╤════════════╤════╗
║ 0A │ 0B         │ 0C ║
╟────┼────────────┼────╢
║ 1A │ 1B         │ 1C ║
╟────┼────────────┼────╢
║ 2A │ 2B         │ 2C ║
╚════╧════════════╧════╝

{object} config.border property describes characters used to draw the table border.

let config,
    data,
    output;
 
data = [
    ['0A', '0B', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C']
];
 
config = {
    border: {
        topBody: ``,
        topJoin: ``,
        topLeft: ``,
        topRight: ``,
 
        bottomBody: ``,
        bottomJoin: ``,
        bottomLeft: ``,
        bottomRight: ``,
 
        bodyLeft: ``,
        bodyRight: ``,
        bodyJoin: ``,
 
        joinBody: ``,
        joinLeft: ``,
        joinRight: ``,
        joinJoin: ``
    }
};
 
output = table(data, config);
 
console.log(output);
┌────┬────┬────┐
│ 0A │ 0B │ 0C │
├────┼────┼────┤
│ 1A │ 1B │ 1C │
├────┼────┼────┤
│ 2A │ 2B │ 2C │
└────┴────┴────┘

{function} config.drawHorizontalLine property is a function that is called for every non-content row in the table. The result of the function {boolean} determines whether a row is drawn.

let data,
    output,
    options;
 
data = [
    ['0A', '0B', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C'],
    ['3A', '3B', '3C'],
    ['4A', '4B', '4C']
];
 
options = {
    /**
     * @typedef {function} drawJoin 
     * @param {number} index 
     * @param {number} size 
     * @return {boolean}
     */
    drawHorizontalLine: (index, size) => {
        return index === 0 || index === 1 || index === size - 1 || index === size;
    }
};
 
output = table(data, options);
 
console.log(output);
╔════╤════╤════╗
║ 0A │ 0B │ 0C ║
╟────┼────┼────╢
║ 1A │ 1B │ 1C ║
║ 2A │ 2B │ 2C ║
║ 3A │ 3B │ 3C ║
╟────┼────┼────╢
║ 4A │ 4B │ 4C ║
╚════╧════╧════╝

{number} config.columns[{number}].paddingLeft and {number} config.columns[{number}].paddingRight properties control content padding within a cell. Property value represents a number of whitespaces used to pad the content.

let config,
    data,
    output;
 
data = [
    ['0A', 'AABBCC', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C']
];
 
config = {
    columns: {
        0: {
            paddingLeft: 3
        },
        1: {
            width: 2,
            paddingRight: 3
        }
    }
};
 
output = table(data, config);
 
console.log(output);
╔══════╤══════╤════╗
║   0A │ AA   │ 0C ║
║      │ BB   │    ║
║      │ CC   │    ║
╟──────┼──────┼────╢
║   1A │ 1B   │ 1C ║
╟──────┼──────┼────╢
║   2A │ 2B   │ 2C ║
╚══════╧══════╧════╝

You can load one of the predefined border templates using getBorderCharacters function.

import {
    table,
    getBorderCharacters
} from 'table';
 
let config,
    data;
 
data = [
    ['0A', '0B', '0C'],
    ['1A', '1B', '1C'],
    ['2A', '2B', '2C']
];
 
config = {
    border: getBorderCharacters(`name of the template`)
};
 
table(data, config);
# honeywell
 
╔════╤════╤════╗
║ 0A │ 0B │ 0C ║
╟────┼────┼────╢
║ 1A │ 1B │ 1C ║
╟────┼────┼────╢
║ 2A │ 2B │ 2C ║
╚════╧════╧════╝
 
# norc
 
┌────┬────┬────┐
│ 0A │ 0B │ 0C │
├────┼────┼────┤
│ 1A │ 1B │ 1C │
├────┼────┼────┤
│ 2A │ 2B │ 2C │
└────┴────┴────┘
 
# ramac (ASCII; for use in terminals that do not support Unicode characters)
 
+----+----+----+
| 0A | 0B | 0C |
|----|----|----|
| 1A | 1B | 1C |
|----|----|----|
| 2A | 2B | 2C |
+----+----+----+
 
# void (no borders; see "bordless table" section of the documentation)
 
 0A  0B  0C
 
 1A  1B  1C
 
 2A  2B  2C
 

Raise an issue if you'd like to contribute a new border template.

Simply using "void" border character template creates a table with a lot of unnecessary spacing.

To create a more plesant to the eye table, reset the padding and remove the joining rows, e.g.

let output;
 
output = table(data, {
    border: getBorderCharacters(`void`),
    columnDefault: {
        paddingLeft: 0,
        paddingRight: 1
    },
    drawJoin: () => {
        return false
    }
});
 
console.log(output);
0A 0B 0C
1A 1B 1C
2A 2B 2C

table package exports createStream function used to draw a table and append rows.

createStream requires {number} columnDefault.width and {number} columnCount configuration properties.

import {
    createStream
} from 'table';
 
let config,
    stream;
 
config = {
    columnDefault: {
        width: 50
    },
    columnCount: 1
};
 
stream = createStream(config);
 
setInterval(() => {
    stream.write([new Date()]);
}, 500);

table package uses ANSI escape codes to overwrite the output of the last line when a new row is printed.

The underlying implementation is explained in this Stack Overflow answer.

Streaming supports all of the configuration properties and functionality of a static table (such as auto text wrapping, alignment and padding), e.g.

import {
    createStream
} from 'table';
 
import _ from 'lodash';
 
let config,
    stream,
    i;
 
config = {
    columnDefault: {
        width: 50
    },
    columnCount: 3,
    columns: {
        0: {
            width: 10,
            alignment: 'right'
        },
        1: {
            alignment: 'center',
        },
        2: {
            width: 10
        }
    }
};
 
stream = createStream(config);
 
= 0;
 
setInterval(() => {
    let random;
 
    random = _.sample('abcdefghijklmnopqrstuvwxyz', _.random(1, 30)).join('');
 
    stream.write([i++, new Date(), random]);
}, 500);

To handle a content that overflows the container width, table package implements text wrapping. However, sometimes you may want to truncate content that is too long to be displayed in the table.

{number} config.columns[{number}].truncate property (default: Infinity) truncates the text at the specified length.

let config,
    data,
    output;
 
data = [
    ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.']
];
 
config = {
    columns: {
        0: {
            width: 20,
            truncate: 100
        }
    }
};
 
output = table(data, config);
 
console.log(output);
╔══════════════════════╗
║ Lorem ipsum dolor si ║
║ t amet, consectetur  ║
║ adipiscing elit. Pha ║
║ sellus pulvinar nibh ║
║ sed mauris conva...  ║
╚══════════════════════╝

table package implements auto text wrapping, i.e. text that has width greater than the container width will be separated into multiple lines, e.g.

let config,
    data,
    output;
 
data = [
    ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.']
];
 
config = {
    columns: {
        0: {
            width: 20
        }
    }
};
 
output = table(data, config);
 
console.log(output);
╔══════════════════════╗
║ Lorem ipsum dolor si ║
║ t amet, consectetur  ║
║ adipiscing elit. Pha ║
║ sellus pulvinar nibh ║
║ sed mauris convallis ║
║ dapibus. Nunc venena ║
║ tis tempus nulla sit ║
║ amet viverra.        ║
╚══════════════════════╝

When wrapWord is true the text is broken at the nearest space or one of the special characters ("-", "_", "", "/", ".", ",", ";"), e.g.

let config,
    data,
    output;
 
data = [
    ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.']
];
 
config = {
    columns: {
        0: {
            width: 20,
            wrapWord: true
        }
    }
};
 
output = table(data, config);
 
console.log(output);
╔══════════════════════╗
║ Lorem ipsum dolor    ║
║ sit amet,            ║
║ consectetur          ║
║ adipiscing elit.     ║
║ Phasellus pulvinar   ║
║ nibh sed mauris      ║
║ convallis dapibus.   ║
║ Nunc venenatis       ║
║ tempus nulla sit     ║
║ amet viverra.        ║
╚══════════════════════╝