string-table
Advanced tools
| { | ||
| "name": "stringTable.js", | ||
| "repo": "dtao/stringTable.js", | ||
| "description": "Format an array of data objects as a textual table", | ||
| "version": "0.1.5", | ||
| "keywords": [], | ||
| "dependencies": {}, | ||
| "development": {}, | ||
| "license": "MIT", | ||
| "main": "stringTable.js", | ||
| "scripts": [ | ||
| "stringTable.js" | ||
| ] | ||
| } |
+22
| Copyright (c) 2013 Dan Tao | ||
| MIT License | ||
| Permission is hereby granted, free of charge, to any person obtaining | ||
| a copy of this software and associated documentation files (the | ||
| "Software"), to deal in the Software without restriction, including | ||
| without limitation the rights to use, copy, modify, merge, publish, | ||
| distribute, sublicense, and/or sell copies of the Software, and to | ||
| permit persons to whom the Software is furnished to do so, subject to | ||
| the following conditions: | ||
| The above copyright notice and this permission notice shall be | ||
| included in all copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+2
-2
| { | ||
| "name": "string-table", | ||
| "main": "stringTable.js", | ||
| "version": "0.1.3", | ||
| "version": "0.1.5", | ||
| "homepage": "https://github.com/dtao/stringTable.js", | ||
@@ -9,4 +9,4 @@ "authors": [ | ||
| ], | ||
| "description": "Formats an array of data objects as a textual table", | ||
| "description": "Format an array of data objects as a textual table", | ||
| "license": "MIT" | ||
| } |
+4
-5
| { | ||
| "name": "string-table", | ||
| "version": "0.1.3", | ||
| "description": "Formats an array of data objects as a textual table", | ||
| "version": "0.1.5", | ||
| "description": "Format an array of data objects as a textual table", | ||
| "main": "stringTable.js", | ||
@@ -19,6 +19,5 @@ "scripts": { | ||
| }, | ||
| "devDependencies": { | ||
| "colors": ">= 0.6.2", | ||
| "diff": ">= 1.0.7" | ||
| "dependencies": { | ||
| "colors": ">= 0.6.2" | ||
| } | ||
| } |
+98
-16
| stringTable.js | ||
| ============== | ||
| A groundbreaking, innovative JavaScript library to do something that's literally never been attempted before: formatting an array of data objects as a textual table. | ||
| [](https://travis-ci.org/dtao/stringTable.js) | ||
| A groundbreaking, innovative JavaScript library to do something that's [literally](https://github.com/JanGorman/node-table) [never](https://github.com/eldargab/easy-table) [been](https://github.com/substack/text-table) [attempted](https://github.com/sorensen/ascii-table) before: formatting an array of data objects as a textual table. | ||
| Installation | ||
| ------------ | ||
| npm install string-table | ||
| Example | ||
| ------- | ||
| ```javascript | ||
@@ -13,8 +23,6 @@ var users = [ | ||
| var table = stringTable.create(users); | ||
| stringTable.create(users); | ||
| console.log(table); | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -29,2 +37,46 @@ * | name | gender | age | | ||
| It works with multi-line strings, too! | ||
| ```coffeescript | ||
| # This example is in CoffeeScript for readability. | ||
| books = [ | ||
| { | ||
| title: 'The Cat in the Hat', | ||
| opening: | ||
| """ | ||
| The sun did not shine. | ||
| It was too wet to play. | ||
| So we sat in the house | ||
| All that cold, cold, wet day. | ||
| """ | ||
| }, | ||
| { | ||
| title: 'Green Eggs and Ham', | ||
| opening: | ||
| """ | ||
| I am Sam. | ||
| Sam I am. | ||
| Do you like green eggs and ham? | ||
| """ | ||
| } | ||
| ] | ||
| stringTable.create(books) | ||
| # | ||
| # Result: | ||
| # | ||
| # | title | opening | | ||
| # -------------------------------------------------------- | ||
| # | The Cat in the Hat | The sun did not shine. | | ||
| # | | It was too wet to play. | | ||
| # | | So we sat in the house | | ||
| # | | All that cold, cold, wet day. | | ||
| # | Green Eggs and Ham | I am Sam. | | ||
| # | | Sam I am. | | ||
| # | | Do you like green eggs and ham? | | ||
| # | ||
| ``` | ||
| You can also specify options to customize how the table is formatted: | ||
@@ -53,3 +105,3 @@ | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -76,3 +128,3 @@ * | age | name | | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -89,3 +141,3 @@ * | Name | Gender | Age | | ||
| An object mapping column names to formatter functions | ||
| An object mapping column names to formatter functions, which will accept `(value, header)` arguments | ||
@@ -99,3 +151,3 @@ *Default: none* | ||
| formatters: { | ||
| name: function(value) { return value.toUpperCase(); } | ||
| name: function(value, header) { return value.toUpperCase(); } | ||
| } | ||
@@ -105,3 +157,3 @@ }); | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -116,2 +168,32 @@ * | name | gender | age | | ||
| A formatter may also return an object with the properties `{ value, format }`, where `format` in turn can have the properties `{ color, alignment }`. | ||
| ```javascript | ||
| stringTable.create(users, { | ||
| formatters: { | ||
| gender: function(value, header) { | ||
| return { | ||
| value: value, | ||
| format: { | ||
| color: value === 'M' ? 'cyan' : 'magenta', | ||
| alignment: 'right' | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| }); | ||
| /* | ||
| * Result: | ||
| * | ||
| * | name | gender | age | | ||
| * ---------------------------- | ||
| * | Dan | M | 29.00 | | ||
| * | Adam | M | 31.00 | | ||
| * | Lauren | F | 33.00 | | ||
| * | ||
| * (Imagine the Ms are cyan and the F is magenta above.) | ||
| */ | ||
| ``` | ||
| ### `typeFormatters` | ||
@@ -128,3 +210,3 @@ | ||
| typeFormatters: { | ||
| number: function(value) { return value.toFixed(2); } | ||
| number: function(value, header) { return value.toFixed(2); } | ||
| } | ||
@@ -134,3 +216,3 @@ }); | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -160,3 +242,3 @@ * | name | gender | age | | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -175,3 +257,3 @@ * % name $ gender $ age % | ||
| *Default: `undefined`* | ||
| *Default: none* | ||
@@ -184,3 +266,3 @@ #### Example | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -209,3 +291,3 @@ * | name | gender | age | | ||
| /* | ||
| * Output: | ||
| * Result: | ||
| * | ||
@@ -212,0 +294,0 @@ * | name | gender | age | |
+156
-16
| require('colors') | ||
| jsDiff = require('diff') | ||
| stringTable = require('../stringTable.js') | ||
@@ -7,15 +6,10 @@ | ||
| juxtapose = (left, right, indentation) -> | ||
| [leftRows, rightRows] = [left.split('\n'), right.split('\n')] | ||
| [leftWidth, rightWidth] = [leftRows[0].length, rightRows[0].length] | ||
| [leftRows, rightRows] = [left.split('\n'), right.split('\n')] | ||
| leftWidth = leftRows[0].length | ||
| output = for i in [0...Math.max(leftRows.length, rightRows.length)] | ||
| changes = jsDiff.diffWords(leftRows[i] || '', rightRows[i] || '') | ||
| parts = for change in changes | ||
| if change.added | ||
| change.value.green | ||
| else if change.removed | ||
| change.value.red | ||
| else | ||
| change.value | ||
| indent(indentation) + parts.join('') | ||
| [ | ||
| pad(leftRows[i] || '', leftWidth), | ||
| rightRows[i] || '' | ||
| ].join(indent(indentation)) | ||
@@ -27,2 +21,5 @@ output.join('\n') | ||
| pad = (str, width) -> | ||
| return str + indent(width - str.length) | ||
| beforeEach -> | ||
@@ -206,2 +203,108 @@ this.addMatchers | ||
| describe 'when receiving a { value, format } object as formatter output', -> | ||
| users = [ | ||
| { name: 'Dan', gender: 'M', age: 29 }, | ||
| { name: 'Adam', gender: 'M', age: 31 }, | ||
| { name: 'Lauren', gender: 'F', age: 33 } | ||
| ] | ||
| it 'applies no formatting if none is given', -> | ||
| options = | ||
| formatters: | ||
| gender: (value) -> | ||
| value: value | ||
| expect(stringTable.create(users, options)).toMatchTable( | ||
| """ | ||
| | name | gender | age | | ||
| ------------------------- | ||
| | Dan | M | 29 | | ||
| | Adam | M | 31 | | ||
| | Lauren | F | 33 | | ||
| """ | ||
| ) | ||
| it 'applies colors, if specified', -> | ||
| options = | ||
| formatters: | ||
| gender: (value) -> | ||
| value: value | ||
| format: | ||
| color: if value == 'M' then 'cyan' else 'magenta' | ||
| expect(stringTable.create(users, options)).toMatchTable( | ||
| """ | ||
| | name | gender | age | | ||
| ------------------------- | ||
| | Dan | #{'M'.cyan} | 29 | | ||
| | Adam | #{'M'.cyan} | 31 | | ||
| | Lauren | #{'F'.magenta} | 33 | | ||
| """ | ||
| ) | ||
| it 'applies alignment, if specified', -> | ||
| options = | ||
| formatters: | ||
| gender: (value) -> | ||
| value: value | ||
| format: | ||
| alignment: 'right' | ||
| expect(stringTable.create(users, options)).toMatchTable( | ||
| """ | ||
| | name | gender | age | | ||
| ------------------------- | ||
| | Dan | M | 29 | | ||
| | Adam | M | 31 | | ||
| | Lauren | F | 33 | | ||
| """ | ||
| ) | ||
| it 'applies alignment to multi-line values as well', -> | ||
| books = [ | ||
| { | ||
| title: 'The Cat in the Hat', | ||
| opening: | ||
| """ | ||
| The sun did not shine. | ||
| It was too wet to play. | ||
| So we sat in the house | ||
| All that cold, cold, wet day. | ||
| """ | ||
| }, | ||
| { | ||
| title: 'Green Eggs and Ham', | ||
| opening: | ||
| """ | ||
| I am Sam. | ||
| Sam I am. | ||
| Do you like green eggs and ham? | ||
| """ | ||
| } | ||
| ] | ||
| options = | ||
| formatters: | ||
| opening: (value) -> | ||
| value: value | ||
| format: | ||
| alignment: 'right' | ||
| headerSeparator: '=' | ||
| rowSeparator: '-' | ||
| expect(stringTable.create(books, options)).toMatchTable( | ||
| """ | ||
| | title | opening | | ||
| ======================================================== | ||
| | The Cat in the Hat | The sun did not shine. | | ||
| | | It was too wet to play. | | ||
| | | So we sat in the house | | ||
| | | All that cold, cold, wet day. | | ||
| -------------------------------------------------------- | ||
| | Green Eggs and Ham | I am Sam. | | ||
| | | Sam I am. | | ||
| | | Do you like green eggs and ham? | | ||
| """ | ||
| ) | ||
| it 'allows you to specify a custom formatter for a given type', -> | ||
@@ -228,2 +331,24 @@ numbers = [ | ||
| it 'passes both value and column header to custom type formatters', -> | ||
| people = [ | ||
| { firstName: 'Dan', lastName: 'Tao', middleInitial: 'L' }, | ||
| { firstName: 'Joe', lastName: 'Schmoe', middleInitial: 'K' }, | ||
| { firstName: 'Johnny', lastName: 'Public', middleInitial: 'Q' } | ||
| ] | ||
| options = | ||
| typeFormatters: | ||
| string: (value, heading) -> | ||
| if heading.match(/Name$/) then value.toUpperCase() else value.toLowerCase() | ||
| expect(stringTable.create(people, options)).toMatchTable( | ||
| """ | ||
| | firstName | lastName | middleInitial | | ||
| ---------------------------------------- | ||
| | DAN | TAO | l | | ||
| | JOE | SCHMOE | k | | ||
| | JOHNNY | PUBLIC | q | | ||
| """ | ||
| ) | ||
| it 'gives precedence to a column-specific formatter before a type formatter', -> | ||
@@ -245,8 +370,23 @@ options = | ||
| it 'by default, outputs the strings "null" and "undefined" for null an undefined', -> | ||
| objects[0].a = null | ||
| objects[0].b = undefined | ||
| options = | ||
| formatters: | ||
| a: (value) -> value | ||
| b: (value) -> value | ||
| expect(stringTable.create(objects, options)).toMatchTable( | ||
| """ | ||
| | a | b | c | | ||
| -------------------------- | ||
| | null | undefined | cow | | ||
| | arc | bra | cap | | ||
| """ | ||
| ) | ||
| it 'can adjust column widths for colored output', -> | ||
| require('colors') | ||
| options = | ||
| adjustForColoredOutput: true | ||
| palette = [ | ||
@@ -259,3 +399,3 @@ { name: 'success', color: 'green'.green }, | ||
| expect(stringTable.create(palette, options)).toMatchTable( | ||
| expect(stringTable.create(palette)).toMatchTable( | ||
| """ | ||
@@ -262,0 +402,0 @@ | name | color | |
+57
-30
@@ -18,7 +18,8 @@ (function(module) { | ||
| typeFormatters = options.typeFormatters || {}, | ||
| coloredOutput = options.adjustForColoredOutput || false, | ||
| rows = [createHeaderRow(headers, capitalizeHeaders)]; | ||
| rows = [createHeaderRow(headers, capitalizeHeaders)], | ||
| formats = []; | ||
| for (var i = 0; i < records.length; ++i) { | ||
| rows.push(createRow(records[i], headers, formatters, typeFormatters)); | ||
| // Yes, this is getting completely out of hand. | ||
| appendRowAndFormat(records[i], headers, formatters, typeFormatters, rows, formats); | ||
| } | ||
@@ -28,6 +29,6 @@ | ||
| // Width of outer border on each side | ||
| (strLength(outerBorder, coloredOutput) * 2) + | ||
| (strLength(outerBorder) * 2) + | ||
| // There will be an inner border between each cell, hence 1 fewer than total # of cells | ||
| (strLength(innerBorder, coloredOutput) * (headers.length - 1)) + | ||
| (strLength(innerBorder) * (headers.length - 1)) + | ||
@@ -40,3 +41,3 @@ // Each cell is padded by an additional space on either side | ||
| (function(columnIndex) { | ||
| var columnWidth = getMaxWidth(rows, columnIndex, coloredOutput); | ||
| var columnWidth = getMaxWidth(rows, columnIndex); | ||
| columnWidths.push(columnWidth); | ||
@@ -54,3 +55,3 @@ totalWidth += columnWidth; | ||
| for (var i = 0; i < rows.length; ++i) { | ||
| (function(row) { | ||
| (function(row, cellFormats) { | ||
| // Determine the height of each row | ||
@@ -60,2 +61,9 @@ var rowHeight = getMaxHeight(row), | ||
| // Get the lines of each cell once, so we don't have to keep splitting | ||
| // over and over in the loop after this one. | ||
| var cellLines = []; | ||
| for (var col = 0; col < row.length; ++col) { | ||
| cellLines.push(String(row[col]).split('\n')); | ||
| } | ||
| // Print the row one line at a time (this requires revisiting each cell N times for N lines) | ||
@@ -66,6 +74,6 @@ for (var line = 0; line < rowHeight; ++line) { | ||
| for (var j = 0; j < row.length; ++j) { | ||
| (function(cell, width, type) { | ||
| var cellLines = String(cell).split('\n'); | ||
| currentLine.push(formatCell(cellLines[line] || '', width, type, coloredOutput)); | ||
| }(row[j], columnWidths[j], columnTypes[j])); | ||
| (function(cell, width, type, format) { | ||
| var lines = cellLines[j]; | ||
| currentLine.push(formatCell(lines[line] || '', width, type, format)); | ||
| }(row[j], columnWidths[j], columnTypes[j], i > 0 && cellFormats[j])); | ||
| } | ||
@@ -84,3 +92,3 @@ | ||
| } | ||
| }(rows[i])); | ||
| }(rows[i], formats[i - 1])); | ||
| } | ||
@@ -91,4 +99,6 @@ | ||
| function createRow(data, headers, formatters, typeFormatters) { | ||
| var row = []; | ||
| function appendRowAndFormat(data, headers, formatters, typeFormatters, rows, formats) { | ||
| var row = [], | ||
| cellFormats = []; | ||
| for (var i = 0; i < headers.length; ++i) { | ||
@@ -102,7 +112,22 @@ (function(header, columnIndex) { | ||
| row.push(formatter(value)); | ||
| var formatted = formatter(value, header); | ||
| if (formatted && typeof formatted === 'object') { | ||
| value = formatted.value; | ||
| if (formatted.format && formatted.format.color) { | ||
| value = value[formatted.format.color]; | ||
| } | ||
| cellFormats.push(formatted.format); | ||
| } else { | ||
| value = formatted; | ||
| cellFormats.push(null); | ||
| } | ||
| row.push(value); | ||
| }(headers[i], i)); | ||
| } | ||
| return row; | ||
| rows.push(row); | ||
| formats.push(cellFormats); | ||
| } | ||
@@ -124,3 +149,3 @@ | ||
| function getMaxWidth(rows, columnIndex, coloredOutput) { | ||
| function getMaxWidth(rows, columnIndex) { | ||
| var maxWidth = 0, | ||
@@ -131,3 +156,3 @@ lines; | ||
| for (var j = 0; j < lines.length; ++j) { | ||
| maxWidth = Math.max(maxWidth, strLength(lines[j], coloredOutput)); | ||
| maxWidth = Math.max(maxWidth, strLength(lines[j])); | ||
| } | ||
@@ -158,20 +183,22 @@ } | ||
| function formatCell(value, width, type, coloredOutput) { | ||
| var padding = width - strLength(value, coloredOutput); | ||
| function formatCell(value, width, type, format) { | ||
| var padding = width - strLength(value); | ||
| if (type === 'string') { | ||
| return padLeft(value, padding); | ||
| } | ||
| var alignment = (format && format.alignment) || | ||
| (type === 'number' ? 'right' : 'left'); | ||
| return padRight(value, padding); | ||
| } | ||
| switch (alignment) { | ||
| case 'right': | ||
| return padRight(value, padding); | ||
| function strLength(value, coloredOutput) { | ||
| var str = String(value); | ||
| if (coloredOutput) { | ||
| str = str.replace(/\u001b\[\d{1,2}m?/g, ''); | ||
| case 'left': | ||
| default: | ||
| return padLeft(value, padding); | ||
| } | ||
| return str.length; | ||
| } | ||
| function strLength(value) { | ||
| return String(value).replace(/\u001b\[\d{1,2}m?/g, '').length; | ||
| } | ||
| /** | ||
@@ -178,0 +205,0 @@ * @examples |
AI-detected possible typosquat
Supply chain riskAI has identified this package as a potential typosquat of a more popular package. This suggests that the package may be intentionally mimicking another package's name, description, or other metadata.
Found 1 instance in 1 package
27664
47.1%0
-100%9
28.57%252
16.13%287
40%1
Infinity%1
Infinity%+ Added
+ Added