From 5a0a1cc2f43f74b720edce77470445b29a5781d9 Mon Sep 17 00:00:00 2001 From: Stephan Smith Date: Sat, 1 Aug 2020 17:18:08 -0400 Subject: [PATCH] Added header objects and defaults --- index.js | 24 +++-- package-lock.json | 219 ++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 21 +++++ test.js | 55 ++++++++++++ 4 files changed, 314 insertions(+), 5 deletions(-) create mode 100644 package-lock.json diff --git a/index.js b/index.js index e550bd4..9733f9f 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ var gen = require('generate-object-property') var CsvWriteStream = function(opts) { if (!opts) opts = {} - stream.Transform.call(this, {objectMode:true, highWaterMark:16}) + stream.Transform.call(this, { objectMode:true, highWaterMark:16 }) this.sendHeaders = opts.sendHeaders !== false this.headers = opts.headers || null @@ -25,9 +25,15 @@ CsvWriteStream.prototype._compile = function(headers) { var str = 'function toRow(obj) {\n' if (!headers.length) str += '""' + var rawheaders = this.headers; headers = headers.map(function(prop, i) { - str += 'var a'+i+' = '+prop+' == null ? "" : '+prop+'\n' + if (typeof rawheaders[i] === 'object') { + var def = rawheaders[i].default || '' + str += 'var a'+i+' = '+prop+' == null ? "'+def+'" : '+prop+'\n' + } else { + str += 'var a'+i+' = '+prop+' == null ? "" : '+prop+'\n' + } return 'a'+i }) @@ -58,14 +64,22 @@ CsvWriteStream.prototype._transform = function(row, enc, cb) { var heads = [] for (var i = 0; i < this.headers.length; i++) { - arrProps.push('obj['+i+']') - objProps.push(gen('obj', this.headers[i])) + + if (typeof this.headers[i] === 'object') { + arrProps.push('obj['+i+']') + objProps.push(gen('obj', this.headers[i].field)) + heads.push(this.headers[i].title || this.headers[i].field) + } else { + arrProps.push('obj['+i+']') + objProps.push(gen('obj', this.headers[i])) + heads.push(this.headers[i]) + } } this._objRow = this._compile(objProps) this._arrRow = this._compile(arrProps) - if (this.sendHeaders) this.push(this._arrRow(this.headers)) + if (this.sendHeaders) this.push(this._arrRow(heads)) } if (isArray) { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0ce3ff2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,219 @@ +{ + "name": "csv-write-stream", + "version": "2.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "concat-stream": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.11.tgz", + "integrity": "sha512-X3JMh8+4je3U1cQpG87+f9lXHDrqcb2MVLg9L7o8b1UZ0DzhRrUpdn65ttzu10PpJPPI3MQNkis+oha6TSA9Mw==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.9", + "typedarray": "~0.0.5" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "deep-equal": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=", + "dev": true + }, + "defined": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", + "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "requires": { + "is-property": "^1.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "ndjson": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-1.5.0.tgz", + "integrity": "sha1-rmA7NrE0vOw0e0UkIrC/mNWDLsg=", + "requires": { + "json-stringify-safe": "^5.0.1", + "minimist": "^1.2.0", + "split2": "^2.1.0", + "through2": "^2.0.3" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "dev": true, + "requires": { + "through": "~2.3.4" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "requires": { + "through2": "^2.0.2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "tape": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz", + "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=", + "dev": true, + "requires": { + "deep-equal": "~0.1.0", + "defined": "~0.0.0", + "inherits": "~2.0.1", + "jsonify": "~0.0.0", + "resumer": "~0.0.0", + "through": "~2.3.4" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/readme.md b/readme.md index a5d0bb5..9bc60ad 100644 --- a/readme.md +++ b/readme.md @@ -32,6 +32,8 @@ var writer = csvWriter() `headers` can be an array of strings to use as the header row. if you don't specify a header row the keys of the first row written to the stream will be used as the header row IF the first row is an object (see the test suite for more details). if the `sendHeaders` option is set to false, the headers will be used for ordering the data but will never be written to the stream. +`headers` can also be an array of objects containing a header title, field and default. The `title` will replace the field in header. The `field` defines the object key. The `default` set the default value if a key is not found in a given row. + example of auto headers: ```javascript @@ -65,6 +67,25 @@ writer.end() // produces: world,bar,taco\n ``` +example of with headers objects (title, field and defaults): +note: header objects does not work in the cli. + +```javascript +var headers = [ + { title: 'SSN', field: 'ssn_value', default: 'NA'}, + { title: 'First Name', field: 'fName', default: ''}, + { title: 'Last Name', field: 'lName', default: ''} +] +var writer = csvWriter({ headers: headers }) +writer.pipe(fs.createWriteStream('out.csv')) +writer.write({ fName: 'Joe', lName: 'Dirt', ssn_value: '111-22-3333'}) +writer.end() + +// produces: +// SSN,First Name,Last Name\n +// 000-11-0000,Joe,Dirt\n +``` + see the test suite for more examples ## run the test suite diff --git a/test.js b/test.js index 7fec402..6f6b60b 100644 --- a/test.js +++ b/test.js @@ -170,3 +170,58 @@ test('lots of cols', function (t) { writer.write(obj) writer.end() }) + +test('header objects', function(t) { + + var headers = [ + { title: 'My Foo', field: 'foo' }, + { title: 'My Bar', field: 'bar' } + ] + + var writer = csv({ headers: headers }) + + writer.pipe(concat(function(data) { + t.equal('My Foo,My Bar\nfoo,bar\n', data.toString()) + t.end() + })) + + writer.write({ foo: 'foo', bar: 'bar' }) + writer.end() +}) + +test('header objects with basic array', function(t) { + + var headers = [ + { title: 'My Foo', field: 'foo' }, + { title: 'My Bar', field: 'bar' } + ] + + var writer = csv({ headers: headers }) + + writer.pipe(concat(function(data) { + t.equal('My Foo,My Bar\nfoo,bar\n', data.toString()) + t.end() + })) + + writer.write(["foo", "bar"]) + writer.end() +}) + +test('header objects with default with basic array', function(t) { + + var headers = [ + { title: 'My Foo', field: 'foo' }, + { title: 'My Bar', field: 'bar' }, + { title: 'My Bad', field: 'bad', default: '--' } + ] + + var writer = csv({ headers: headers }) + + writer.pipe(concat(function(data) { + t.equal('My Foo,My Bar,My Bad\nfoo,bar,--\n', data.toString()) + t.end() + })) + + writer.write(["foo", "bar"]) + writer.end() +})