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" | ||
} | ||
} |
205
README.md
# 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 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
15588
0
369
1
4
227