Comparing version 0.0.3 to 0.2.0
@@ -1,2 +0,2 @@ | ||
module.exports = sort; | ||
module.exports = sort | ||
@@ -7,7 +7,7 @@ function sort (comparator) { | ||
? comparator | ||
: Object.keys(this.columns); | ||
comparator = KeysComparator(sortKeys); | ||
: Object.keys(this.columns) | ||
comparator = KeysComparator(sortKeys) | ||
} | ||
this.lines.sort(comparator); | ||
return this; | ||
this.rows.sort(comparator) | ||
return this | ||
} | ||
@@ -17,8 +17,8 @@ | ||
var comparators = keys.map(function (key) { | ||
var sortFn = 'asc'; | ||
var sortFn = 'asc' | ||
var m = /(.*)\|\s*(asc|des)\s*$/.exec(key); | ||
var m = /(.*)\|\s*(asc|des)\s*$/.exec(key) | ||
if (m) { | ||
key = m[1]; | ||
sortFn = m[2]; | ||
key = m[1] | ||
sortFn = m[2] | ||
} | ||
@@ -28,5 +28,5 @@ | ||
var ret = compare(a[key], b[key]) | ||
return sortFn == 'asc' ? ret : -1 * ret; | ||
return sortFn == 'asc' ? ret : -1 * ret | ||
} | ||
}); | ||
}) | ||
@@ -36,5 +36,5 @@ return function (a, b) { | ||
var res = comparators[i](a, b) | ||
if (res != 0) return res; | ||
if (res != 0) return res | ||
} | ||
return 0; | ||
return 0 | ||
} | ||
@@ -44,10 +44,10 @@ } | ||
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)); | ||
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)) | ||
} |
304
lib/table.js
@@ -1,109 +0,259 @@ | ||
module.exports = Table; | ||
module.exports = Table | ||
function Table () { | ||
this.shift = ' '; | ||
this.columns = {}; | ||
this.lines = []; | ||
this.line = {}; | ||
Table.string = function (val) { | ||
if (val === undefined) return '' | ||
return String(val) | ||
} | ||
Table.prototype.cell = function (col, obj, printer, length) { | ||
this.line[col] = obj; | ||
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) | ||
} | ||
} | ||
var c = this.columns[col] || (this.columns[col] = {length: col.length}); | ||
c.printer = printer || Table.string; | ||
if (length != null) { | ||
c.length = length; | ||
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 | ||
} | ||
else { | ||
var s = c.printer(obj); | ||
c.length = s.length > c.length ? s.length : c.length; | ||
} | ||
return this; | ||
} | ||
Table.prototype.newLine = function () { | ||
this.lines.push(this.line); | ||
this.line = {}; | ||
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 | ||
} | ||
} | ||
Table.prototype.sort = require('./sort'); | ||
Table.padLeft = Table.LeftPadder() | ||
Table.printArray = function (arr, format, cb) { | ||
format = typeof format == 'function' ? format : Formatter(format) | ||
cb = cb || function (t) { | ||
return t.toString() | ||
} | ||
Table.prototype.toString = function () { | ||
var self = this; | ||
var padWithDashs = RightPadder('-'); | ||
var t = new Table | ||
var cell = t.cell.bind(t) | ||
var s = ''; | ||
s += this._printLine(function (key) { | ||
return key; | ||
}); | ||
arr.forEach(function (obj) { | ||
format(obj, cell) | ||
t.newRow() | ||
}) | ||
return cb(t) | ||
} | ||
s += this._printLine(function (key, col) { | ||
return padWithDashs('', col.length); | ||
}); | ||
Table.printObj = function (obj, format, cb) { | ||
format = typeof format == 'function' ? format : Formatter(format) | ||
cb = cb || function (t) { | ||
return t.printTransposed(' : ') | ||
} | ||
this.lines.forEach(function (line) { | ||
s += self._printLine(function (key, col) { | ||
return col.printer(line[key], col.length); | ||
}); | ||
}); | ||
return s; | ||
var t = new Table | ||
format(obj, t.cell.bind(t)) | ||
t.newRow() | ||
return cb(t) | ||
} | ||
Table.prototype._printLine = function (print) { | ||
var s = ''; | ||
var firstColumn = true; | ||
for (var key in this.columns) { | ||
if (!firstColumn) s += this.shift; | ||
firstColumn = false; | ||
var col = this.columns[key]; | ||
s += printCell(print(key, col), col.length); | ||
function Formatter (opts) { | ||
opts = opts || {} | ||
return function (obj, cell) { | ||
for (var key in obj) { | ||
var o = opts[key] | ||
cell( | ||
(o && o.name) || key, | ||
obj[key], | ||
o && o.printer, | ||
o && o.width | ||
) | ||
} | ||
} | ||
s += '\n'; | ||
return s; | ||
} | ||
function printCell (s, length) { | ||
if (s.length <= length) return padSpaces(s, length); | ||
s = s.slice(0, length); | ||
if (length > 3) s = s.slice(0, -3).concat('...'); | ||
return s; | ||
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 | ||
} | ||
}) | ||
} | ||
var padSpaces = RightPadder(); | ||
function RightPadder (char) { | ||
char = char || ' '; | ||
return function (obj, length) { | ||
var s = String(obj); | ||
var l = s.length; | ||
for (var i = 0; i < length - l; i++) { | ||
s += char; | ||
Table.print = print | ||
function print (rows, columns, shift) { | ||
var padSpaces = Table.RightPadder() | ||
var widths = {} | ||
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 | ||
} | ||
return s; | ||
} | ||
function cellPrinter (row, col) { | ||
return (row.__printers && row.__printers[col]) || Table.string | ||
} | ||
function calcWidths () { | ||
rows.forEach(function (row) { | ||
for (var key in columns) { | ||
setWidth(key, cellPrinter(row, key).call(row, row[key]).length) | ||
} | ||
}) | ||
} | ||
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 | ||
} | ||
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 | ||
} | ||
calcWidths() | ||
return rows.map(function (row) { | ||
return printRow(function (key, width) { | ||
return cellPrinter(row, key).call(row, row[key], width) | ||
}) | ||
}).join('') | ||
} | ||
function LeftPadder (char) { | ||
char = char || ' '; | ||
return function (obj, length) { | ||
var ret = ''; | ||
var s = String(obj); | ||
for (var i = 0; i < length - s.length; i++) { | ||
ret += char; | ||
function Table () { | ||
this.columns = {} /* @api: public */ | ||
this.rows = [] /* @api: public */ | ||
this._row = new Row | ||
} | ||
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.newRow = Table.prototype.newLine = function () { | ||
this.rows.push(this._row) | ||
this._row = new Row | ||
return this | ||
} | ||
Table.prototype.sort = require('./sort') | ||
Table.aggr = require('./aggregations') | ||
Table.prototype.totals = null /* @api: public */ | ||
Table.prototype.total = function (col, fn, printer) { | ||
fn = fn || Table.aggr.sum | ||
printer = printer || fn.printer | ||
this.totals = this.totals || new Row | ||
var val | ||
var rows = this.rows | ||
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.prototype.shift = ' ' | ||
Table.prototype.print = function () { | ||
return print(this.rows, this.columns, this.shift) | ||
} | ||
Table.prototype.printTransposed = function (delimeter) { | ||
var t = new Table | ||
if (delimeter) t.shift = delimeter | ||
function Printer (row, key) { | ||
var p = row.__printers && row.__printers[key] | ||
if (p) return function (val) { | ||
return p(val) | ||
} | ||
ret += s; | ||
return ret; | ||
} | ||
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() | ||
} | ||
return t.print() | ||
} | ||
Table.padLeft = LeftPadder(); | ||
Table.prototype.toString = function () { | ||
var padWithDashs = Table.RightPadder('-') | ||
var delimeter = this.createRow(function () { | ||
return ['', padWithDashs] | ||
}) | ||
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) | ||
} | ||
Table.string = function (obj) { | ||
if (obj === undefined) return ''; | ||
return String(obj); | ||
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 | ||
} |
@@ -6,3 +6,3 @@ { | ||
"keywords": ["table", "text", "cli"], | ||
"version": "0.0.3", | ||
"version": "0.2.0", | ||
"repository": { | ||
@@ -9,0 +9,0 @@ "type": "git", |
162
README.md
@@ -21,4 +21,4 @@ # Easy table | ||
t.cell('Description', product.desc); | ||
t.cell('Price, USD', product.price.toFixed(2), Table.padLeft); | ||
t.newLine(); | ||
t.cell('Price, USD', product.price, Table.Number(2)); | ||
t.newRow(); | ||
}); | ||
@@ -40,2 +40,22 @@ | ||
`t.printTransposed()` yields | ||
``` | ||
Product Id : 245452 : 232323 : 123123 | ||
Description : Very interesting book : Yet another product : Something awesome | ||
Price, USD : 11.45 : 555.55 : 1000.00 | ||
``` | ||
Finally `t.print()` shows just the rows you pushed and nothing more | ||
``` | ||
245452 Very interesting book 11.45 | ||
232323 Yet another product 555.55 | ||
123123 Something awesome 1000.00 | ||
``` | ||
### How it works | ||
The full signature of `.cell()` method is: | ||
@@ -47,5 +67,2 @@ | ||
Where `column` is a column name to print, `value` - cell's value, `printer` is | ||
a function with which cell's value should be printed, `width` - column's width. | ||
By default column's width is ajusted to fit the longest value, but if specified | ||
@@ -59,2 +76,89 @@ explicitly it is fixed and any non-fitting cell is truncated. | ||
``` 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) | ||
} | ||
... | ||
t.cell('foo', 300, markTooBigs) | ||
``` | ||
### Table.printArray(), Table.printObject() | ||
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. | ||
``` javascript | ||
var array = [ | ||
{foo: 'foo1', bar: 'bar1'}, | ||
{foo: 'foo2', bar: 'bar2'} | ||
] | ||
console.log(Table.printArray(array)) | ||
``` | ||
yields | ||
``` | ||
foo bar | ||
---- ---- | ||
foo1 bar1 | ||
foo2 bar2 | ||
``` | ||
we can pass options to override defaut behaviour | ||
``` javascript | ||
Table.printArray(array, { | ||
bar: { | ||
name: 'Long field name', | ||
printer: Table.padLeft | ||
} | ||
}) | ||
``` | ||
``` | ||
foo Long field name | ||
---- --------------- | ||
foo1 bar1 | ||
foo2 bar2 | ||
``` | ||
or have a full control over rendering | ||
``` javascript | ||
Table.printArray(array, function (obj, cell) { | ||
cell('foo', obj.foo) | ||
cell('field', obj.bar) | ||
}, function (table) { | ||
return table.print() | ||
}) | ||
``` | ||
`Table.printObj()` works in the same manner | ||
``` javascript | ||
var obj = { | ||
foo: 'foo', | ||
bar: 'bar' | ||
} | ||
Table.printObj(obj) | ||
``` | ||
yields | ||
``` | ||
foo : foo | ||
bar : bar | ||
``` | ||
### Sorting | ||
You can sort a table by calling `.sort()`, and optionally passing in a list of | ||
@@ -70,2 +174,43 @@ column names to sort on (by default uses all columns), or a custom comparator | ||
### Totaling | ||
Easy table can help you 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) | ||
}) | ||
``` | ||
yields | ||
``` | ||
Product Id Description Price, USD | ||
---------- --------------------- ----------- | ||
245452 Very interesting book 11.45 | ||
232323 Yet another product 555.55 | ||
123123 Something awesome 1000.00 | ||
---------- --------------------- ----------- | ||
Avg: 522.33 | ||
``` | ||
`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 | ||
@@ -79,2 +224,9 @@ | ||
## 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 | ||
@@ -81,0 +233,0 @@ |
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
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
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
No License Found
License(Experimental) License information could not be found.
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
14447
278
250
6
1
1
1