Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

easy-table

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

easy-table - npm Package Compare versions

Comparing version 0.3.0 to 1.0.0

574

lib/table.js
module.exports = Table
Table.string = function (val) {
if (val === undefined) return ''
return String(val)
function Table() {
this.rows = []
this.row = {__printers : {}}
}
Table.Number = function (digits) {
return function (val, width) {
if (val === undefined) return ''
if (typeof val != 'number')
throw new Error(String(val) + ' is not a number')
var s = digits == null ? String(val) : val.toFixed(digits).toString()
return Table.padLeft(s, width)
}
/**
* Push the current row to the table and start a new one
*
* @returns {Table} `this`
*/
Table.prototype.newRow = function() {
this.rows.push(this.row)
this.row = {__printers : {}}
return this
}
Table.RightPadder = function (char) {
char = char || ' '
return function (val, length) {
var s = String(val)
var l = s.length
for (var i = 0; i < length - l; i++) {
s += char
}
return s
}
/**
* Write cell in the current row
*
* @param {String} col - Column name
* @param {Any} val - Cell value
* @param {Function} [printer] - Printer function to format the value
* @returns {Table} `this`
*/
Table.prototype.cell = function(col, val, printer) {
this.row[col] = val
this.row.__printers[col] = printer || string
return this
}
Table.LeftPadder = function (char) {
char = char || ' '
return function (val, length) {
var ret = ''
var s = String(val)
for (var i = 0; i < length - s.length; i++) {
ret += char
}
ret += s
return ret
}
/**
* String to separate columns
*/
Table.prototype.separator = ' '
function string(val) {
return val === undefined ? '' : ''+val
}
Table.padLeft = Table.LeftPadder()
function length(str) {
return str.replace(/\u001b\[\d+m/g, '').length
}
Table.printArray = function (arr, format, cb) {
format = typeof format == 'function' ? format : Formatter(format)
cb = cb || function (t) {
return t.toString()
}
/**
* Default printer
*/
var t = new Table
var cell = t.cell.bind(t)
Table.string = string
arr.forEach(function (obj) {
format(obj, cell)
t.newRow()
})
return cb(t)
/**
* Create a printer which right aligns the content by padding with `ch` on the left
*
* @param {String} ch
* @returns {Function}
*/
Table.leftPadder = leftPadder
function leftPadder(ch) {
return function(val, width) {
var str = string(val)
var len = length(str)
var pad = width > len ? Array(width - len + 1).join(ch) : ''
return pad + str
}
}
Table.printObj = function (obj, format, cb) {
format = typeof format == 'function' ? format : Formatter(format)
cb = cb || function (t) {
return t.printTransposed(' : ')
}
/**
* Printer which right aligns the content
*/
var t = new Table
format(obj, t.cell.bind(t))
t.newRow()
return cb(t)
var padLeft = Table.padLeft = leftPadder(' ')
/**
* Create a printer which pads with `ch` on the right
*
* @param {String} ch
* @returns {Function}
*/
Table.rightPadder = rightPadder
function rightPadder(ch) {
return function padRight(val, width) {
var str = string(val)
var len = length(str)
var pad = width > len ? Array(width - len + 1).join(ch) : ''
return str + pad
}
}
function Formatter (opts) {
opts = opts || {}
return function (obj, cell) {
for (var key in obj) {
if (!obj.hasOwnProperty(key)) continue
var o = opts[key]
cell(
(o && o.name) || key,
obj[key],
o && o.printer,
o && o.width
)
}
}
var padRight = rightPadder(' ')
/**
* Create a printer for numbers
*
* Will do right alignment and optionally fix the number of digits after decimal point
*
* @param {Number} [digits] - Number of digits for fixpoint notation
* @returns {Function}
*/
Table.number = function(digits) {
return function(val, width) {
if (val == null) return ''
if (typeof val != 'number')
throw new Error(''+val + ' is not a number')
var str = digits == null ? val+'' : val.toFixed(digits)
return padLeft(str, width)
}
}
function each(row, fn) {
for(var key in row) {
if (key == '__printers') continue
fn(key, row[key])
}
}
Table.Row = Row
function Row () {
Object.defineProperties(this, {
__printers: {
value: {},
enumerable: false
},
__cell: {
value: function (col, val, printer) {
this[col] = val
this.__printers[col] = printer
},
enumerable: false
}
/**
* Get list of columns in printing order
*
* @returns {string[]}
*/
Table.prototype.columns = function() {
var cols = {}
for(var i = 0; i < 2; i++) { // do 2 times
this.rows.forEach(function(row) {
var idx = 0
each(row, function(key) {
idx = Math.max(idx, cols[key] || 0)
cols[key] = idx
idx++
})
})
}
return Object.keys(cols).sort(function(a, b) {
return cols[a] - cols[b]
})
}
/**
* Format just rows, i.e. print the table without headers and totals
*
* @returns {String} String representaion of the table
*/
Table.print = print
function print (rows, columns, shift) {
var padSpaces = Table.RightPadder()
var widths = {}
Table.prototype.print = function() {
var cols = this.columns()
var separator = this.separator
var widths = {}
var out = ''
function setWidth (col, width) {
var isFixed = columns[col].width != null
if (isFixed) {
widths[col] = columns[col].width
} else {
if (widths[col] > width) return
widths[col] = width
}
}
// Calc widths
this.rows.forEach(function(row) {
each(row, function(key, val) {
var str = row.__printers[key].call(row, val)
widths[key] = Math.max(length(str), widths[key] || 0)
})
})
function cellPrinter (row, col) {
return (row.__printers && row.__printers[col]) || Table.string
}
// Now print
this.rows.forEach(function(row) {
var line = ''
cols.forEach(function(key) {
var width = widths[key]
var str = row.hasOwnProperty(key)
? ''+row.__printers[key].call(row, row[key], width)
: ''
line += padRight(str, width) + separator
})
line = line.slice(0, -separator.length)
out += line + '\n'
})
function calcWidths () {
rows.forEach(function (row) {
for (var key in columns) {
setWidth(key, cellPrinter(row, key).call(row, row[key]).length)
}
})
}
return out
}
function printRow (cb) {
var s = ''
var firstColumn = true
for (var key in columns) {
if (!firstColumn) s += shift
firstColumn = false
var width = widths[key]
s += printCell(cb(key, width), width)
}
s += '\n'
return s
}
/**
* Format the table
*
* @returns {String}
*/
function printCell (s, width) {
if (s.length <= width) return padSpaces(s, width)
s = s.slice(0, width)
if (width > 3) s = s.slice(0, -3).concat('...')
return s
}
Table.prototype.toString = function() {
var cols = this.columns()
var out = new Table()
calcWidths()
// copy options
out.separator = this.separator
return rows.map(function (row) {
return printRow(function (key, width) {
return cellPrinter(row, key).call(row, row[key], width)
})
}).join('')
// Write header
cols.forEach(function(col) {
out.cell(col, col)
})
out.newRow()
out.pushDelimeter(cols)
// Write body
out.rows = out.rows.concat(this.rows)
// Totals
if (this.totals && this.rows.length) {
out.pushDelimeter(cols)
this.forEachTotal(out.cell.bind(out))
out.newRow()
}
return out.print()
}
/**
* Push delimeter row to the table (with each cell filled with dashs during printing)
*
* @param {String[]} [cols]
* @returns {Table} `this`
*/
function Table () {
this.columns = {} /* @api: public */
this.rows = [] /* @api: public */
this._row = new Row
Table.prototype.pushDelimeter = function(cols) {
cols = cols || this.columns()
cols.forEach(function(col) {
this.cell(col, undefined, leftPadder('-'))
}, this)
return this.newRow()
}
/**
* Compute all totals and yield the results to `cb`
*
* @param {Function} cb - Callback function with signature `(column, value, printer)`
*/
Table.prototype.cell = function (col, val, printer, width) {
this._row.__cell(col, val, printer)
var c = this.columns[col] || (this.columns[col] = {})
if (width != null) c.width = width
return this
Table.prototype.forEachTotal = function(cb) {
for(var key in this.totals) {
var aggr = this.totals[key]
var acc = aggr.init
var len = this.rows.length
this.rows.forEach(function(row, idx) {
acc = aggr.reduce.call(row, acc, row[key], idx, len)
})
cb(key, acc, aggr.printer)
}
}
Table.prototype.newRow = Table.prototype.newLine = function () {
this.rows.push(this._row)
this._row = new Row
/**
* Format the table so that each row represents column and each column represents row
*
* @param {Object} [opts]
* @param {String} [ops.separator] - Column separation string
* @param {Function} [opts.namePrinter] - Printer to format column names
* @returns {String}
*/
Table.prototype.printTransposed = function(opts) {
opts = opts || {}
var out = new Table
out.separator = opts.separator || this.separator
this.columns().forEach(function(col) {
out.cell(0, col, opts.namePrinter)
this.rows.forEach(function(row, idx) {
out.cell(idx+1, row[col], row.__printers[col])
})
out.newRow()
}, this)
return out.print()
}
/**
* Sort the table
*
* @param {Function|string[]} [cmp] - Either compare function or a list of columns to sort on
* @returns {Table} `this`
*/
Table.prototype.sort = function(cmp) {
if (typeof cmp == 'function') {
this.rows.sort(cmp)
return this
}
var keys = Array.isArray(cmp) ? cmp : this.columns()
var comparators = keys.map(function(key) {
var order = 'asc'
var m = /(.*)\|\s*(asc|des)\s*$/.exec(key)
if (m) {
key = m[1]
order = m[2]
}
return function (a, b) {
return order == 'asc'
? compare(a[key], b[key])
: compare(b[key], a[key])
}
})
return this.sort(function(a, b) {
for (var i = 0; i < comparators.length; i++) {
var order = comparators[i](a, b)
if (order != 0) return order
}
return 0
})
}
Table.prototype.sort = require('./sort')
function compare(a, b) {
if (a === b) return 0
if (a === undefined) return 1
if (b === undefined) return -1
if (a === null) return 1
if (b === null) return -1
if (a > b) return 1
if (a < b) return -1
return compare(String(a), String(b))
}
Table.aggr = require('./aggregations')
/**
* Add a total for the column
*
* @param {String} col - column name
* @param {Object} [opts]
* @param {Function} [opts.reduce = sum] - reduce(acc, val, idx, length) function to compute the total value
* @param {Function} [opts.printer = padLeft] - Printer to format the total cell
* @param {Any} [opts.init = 0] - Initial value for reduction
* @returns {Table} `this`
*/
Table.prototype.totals = null /* @api: public */
Table.prototype.total = function(col, opts) {
opts = opts || {}
this.totals = this.totals || {}
this.totals[col] = {
reduce: opts.reduce || Table.aggr.sum,
printer: opts.printer || padLeft,
init: opts.init == null ? 0 : opts.init
}
return this
}
Table.prototype.total = function (col, fn, printer) {
fn = fn || Table.aggr.sum
printer = printer || fn.printer
/**
* Predefined helpers for totals
*/
this.totals = this.totals || new Row
Table.aggr = {}
var val
var rows = this.rows
/**
* Create a printer which formats the value with `printer`,
* adds the `prefix` to it and right aligns the whole thing
*
* @param {String} prefix
* @param {Function} printer
* @returns {printer}
*/
this.totals.__cell(col, null, function (_, width) {
if (width != null) return printer(val, width)
val = rows.reduce(function (val, row, index) {
return fn(val, row[col], index, rows.length)
}, null)
return printer(val)
})
return this
Table.aggr.printer = function(prefix, printer) {
printer = printer || string
return function(val, width) {
return padLeft(prefix + printer(val), width)
}
}
Table.prototype.shift = ' '
/**
* Sum reduction
*/
Table.prototype.print = function () {
return print(this.rows, this.columns, this.shift)
Table.aggr.sum = function(acc, val) {
return acc + val
}
Table.prototype.printTransposed = function (delimeter) {
var t = new Table
if (delimeter) t.shift = delimeter
/**
* Average reduction
*/
function Printer (row, key) {
var p = row.__printers && row.__printers[key]
if (p) return function (val) {
return p(val)
}
}
Table.aggr.avg = function(acc, val, idx, len) {
acc = acc + val
return idx + 1 == len ? acc/len : acc
}
for (var key in this.columns) {
t.cell('h', key)
this.rows.forEach(function (row, index) {
t.cell('f' + index, row[key], Printer(row, key))
})
t.newRow()
/**
* Print the array or object
*
* @param {Array|Object} obj - Object to print
* @param {Function|Object} [format] - Format options
* @param {Function} [cb] - Table post processing and formating
* @returns {String}
*/
Table.print = function(obj, format, cb) {
var opts = format || {}
format = typeof format == 'function'
? format
: function(obj, cell) {
for(var key in obj) {
if (!obj.hasOwnProperty(key)) continue
var params = opts[key] || {}
cell(params.name || key, obj[key], params.printer)
}
}
return t.print()
}
Table.prototype.toString = function () {
var padWithDashs = Table.RightPadder('-')
var delimeter = this.createRow(function () {
return ['', padWithDashs]
var t = new Table
var cell = t.cell.bind(t)
if (Array.isArray(obj)) {
cb = cb || function(t) { return t.toString() }
obj.forEach(function(item) {
format(item, cell)
t.newRow()
})
var head = this.createRow(function (key) {
return [key]
})
var rows = [head, delimeter].concat(this.rows)
if (this.totals) {
rows = rows.concat([delimeter, this.totals])
}
return print(rows, this.columns, this.shift)
} else {
cb = cb || function(t) { return t.printTransposed({separator: ' : '}) }
format(obj, cell)
t.newRow()
}
return cb(t)
}
Table.prototype.createRow = function (cb) {
var row = new Row
for (var key in this.columns) {
var args = cb(key)
row.__cell(key, args[0], args[1])
}
return row
}
/**
* Same as `Table.print()` but yields the result to `console.log()`
*/
Table.log = function(obj, format, cb) {
console.log(Table.print(obj, format, cb))
}
/**
* Same as `.toString()` but yields the result to `console.log()`
*/
Table.prototype.log = function() {
console.log(this.toString())
}

@@ -6,3 +6,3 @@ {

"keywords": ["table", "text", "cli"],
"version": "0.3.0",
"version": "1.0.0",
"repository": {

@@ -16,3 +16,7 @@ "type": "git",

"should": "*"
},
"license": "MIT",
"scripts": {
"test": "mocha -R dot --check-leaks"
}
}
# Easy table
Simple and nice utility for rendering text tables with javascript.
Nice utility for rendering text tables with javascript.
## Usage
``` javascript
var Table = require('easy-table');
```javascript
var Table = require('easy-table')
var data = [
{ id: 123123, desc: 'Something awesome', price: 1000.00 },
{ id: 245452, desc: 'Very interesting book', price: 11.45},
{ id: 232323, desc: 'Yet another product', price: 555.55 }
{ id: 123123, desc: 'Something awesome', price: 1000.00 },
{ id: 245452, desc: 'Very interesting book', price: 11.45},
{ id: 232323, desc: 'Yet another product', price: 555.55 }
]
var t = new Table;
var t = new Table
data.forEach(function (product) {
t.cell('Product Id', product.id);
t.cell('Description', product.desc);
t.cell('Price, USD', product.price, Table.Number(2));
t.newRow();
});
data.forEach(function(product) {
t.cell('Product Id', product.id)
t.cell('Description', product.desc)
t.cell('Price, USD', product.price, Table.number(2))
t.newRow()
})
console.log(t.toString());
console.log(t.toString())
```

@@ -36,6 +36,5 @@

232323 Yet another product 555.55
```
`t.printTransposed()` yields
`t.printTransposed()` returns

@@ -46,12 +45,10 @@ ```

Price, USD : 11.45 : 555.55 : 1000.00
```
Finally `t.print()` shows just the rows you pushed and nothing more
`t.print()` shows just rows you pushed and nothing more
```
123123 Something awesome 1000.00
245452 Very interesting book 11.45
232323 Yet another product 555.55
123123 Something awesome 1000.00
```

@@ -61,61 +58,45 @@

The full signature of `.cell()` method is:
The full signature of `.cell()` is:
``` javascript
t.cell(column, value, printer, width)
```javascript
t.cell(column, value, printer)
```
By default column's width is ajusted to fit the longest value, but if specified
explicitly it is fixed and any non-fitting cell is truncated.
Rendering occures in two phases. At the first phase `printer`
is called to get the minimal width required to fit the cell content.
At the second phase `printer` is called again with
additional `width` parameter to get actual string to render.
Cell's value rendering occures in two phases. At the first phase `printer`
function is called to get minimal width required to fit cell correctly, at the
second phase `printer` function is called to get actual string to render with
additional `width` parameter supplied.
For example, here is how currency printer might be defined
``` javascript
// Example: Coloring too big numbers in red
function markTooBigs (val, width) {
if (width == null) return Table.string(val)
return val > 100
? '\033[31m' + String(val) + '\033[39m'
: Table.string(val)
function currency(val, width) {
var str = val.toFixed(2)
return width ? str : Table.padLeft(str, width)
}
...
t.cell('foo', 300, markTooBigs)
```
### Table.printArray(), Table.printObject()
### Table.print()
Often you just want to print an array or a simple key-value map.
`Table.printArray()` and `Table.printObject()` help to instantiate and fill a table for such use cases.
When you already have an array, explicit table instantiation and iteration
becomes an overhead. For such cases it is convenient to use `Table.print()`.
``` javascript
var array = [
{foo: 'foo1', bar: 'bar1'},
{foo: 'foo2', bar: 'bar2'}
]
console.log(Table.printArray(array))
console.log(Table.print(data))
```
yields
```
foo bar
---- ----
foo1 bar1
foo2 bar2
id desc price
------ --------------------- ------
123123 Something awesome 1000
245452 Very interesting book 11.45
232323 Yet another product 555.55
```
we can pass options to override defaut behaviour
It is possible to pass some options
``` javascript
Table.printArray(array, {
bar: {
name: 'Long field name',
printer: Table.padLeft
}
Table.print(data, {
desc: {name: 'description'}
price: {printer: Table.number(2)}
})

@@ -125,7 +106,7 @@ ```

```
foo Long field name
---- ---------------
foo1 bar1
foo2 bar2
id description price
------ --------------------- -------
123123 Something awesome 1000.00
245452 Very interesting book 11.45
232323 Yet another product 555.55
```

@@ -136,26 +117,20 @@

``` javascript
Table.printArray(array, function (obj, cell) {
cell('foo', obj.foo)
cell('field', obj.bar)
}, function (table) {
return table.print()
Table.print(data, function(item, cell) {
cell('Product id', item.id)
cell('Price, USD', item.price)
}, function(table) {
return table.print()
})
```
`Table.printObj()` works in the same manner
`Table.print()` also accepts objects
``` javascript
var obj = {
foo: 'foo',
bar: 'bar'
}
Table.printObj(obj)
Table.print(data[0])
```
yields
```
foo : foo
bar : bar
id : 123123
desc : Something awesome
price : 1000
```

@@ -177,22 +152,41 @@

Easy table can help you to calculate and render totals:
Easy table can help to calculate and render totals:
``` javascript
t.total('Price, USD', function accumulator (sum, val, index, length) {
sum = sum || 0
sum += val
return index + 1 == length
? sum / length
: sum
}, function print (val, width) {
var s = 'Avg: ' + Table.Number(2)
return width == null
? s
: Table.padLeft(s, width)
})
t.total('Price, USD')
```
yields
```
Product Id Description Price, USD
---------- --------------------- ----------
245452 Very interesting book 11.45
232323 Yet another product 555.55
123123 Something awesome 1000.00
---------- --------------------- ----------
1567.00
```
Here is a more elaborate example
```javascript
t.total('Price, USD', {
printer: Table.aggr.printer('Avg: ', currency),
reduce: Table.aggr.avg,
init: 0
})
// or alternatively
t.total('Price, USD', {
printer: function(val, width) {
return padLeft('Avg: ' + currency(val), width)
},
reduce: function(acc, val, idx, len) {
acc = acc + val
return idx + 1 == len ? acc/len : acc
}
})
```
```
Product Id Description Price, USD

@@ -207,15 +201,5 @@ ---------- --------------------- -----------

`total()` function also accepts printer via `printer` property of
accumulator, so it is possible to create reusable aggregations like:
``` javascript
var priceAvg = // some accumulator
priceAvg.printer = // some printer
...
t.total('Price', 300.50, priceAvg)
```
## Installation
Just install from the npm repository with:
via npm

@@ -226,9 +210,2 @@ ```

## Misc
Easy table now has kind of stable api and exposes many of it's internals.
But in any case it's better to specify strict version numbers in your modules
especially if you use methods or properties not covered by this readme.
## License

@@ -238,3 +215,3 @@

Copyright (c) 2012 Eldar Gabdullin <eldargab@gmail.com>
Copyright (c) 2015 Eldar Gabdullin <eldargab@gmail.com>

@@ -241,0 +218,0 @@ Permission is hereby granted, free of charge, to any person obtaining

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc