UNPKG

csv

Version:

CSV parser with simple api, full of options and tested against large datasets.

251 lines (211 loc) 6.67 kB
// Generated by CoffeeScript 1.6.2 var Transformer, stream; stream = require('stream'); /* Transforming data ================= Transformations may occur synchronously or asynchronously depending on the provided transform callback and its declared arguments length. Callback are called for each line and its arguments are : * *row* CSV record * *index* Incremented counter * *callback* Callback function to be called in asynchronous mode Unless you specify the `columns` read option, the `row` argument will be provided as an array, otherwise it will be provided as an object with keys matching columns names. In synchronous mode, the contract is quite simple, you will receive an array of fields for each record and the transformed array should be returned. In asynchronous mode, it is your responsibility to call the callback provided as the third argument. It must be called with two arguments, the first one is an error if any, the second is the transformed record. Transformed records may be an array, an associative array, a string or null. If null, the record will simply be skipped. When the returned value is an array, the fields are merged in order. When the returned value is an object, it will search for the `columns` property in the write or in the read options and smartly order the values. If no `columns` options are found, it will merge the values in their order of appearance. When the returned value is a string, it is directly sent to the destination source and it is your responsibility to delimit, quote, escape or define line breaks. Transform callback run synchronously: csv() .from('82,Preisner,Zbigniew\n94,Gainsbourg,Serge') .to(console.log) .transform(function(row, index){ return row.reverse() }); // Executing `node samples/transform.js`, print: // 94,Gainsbourg,Serge\n82,Preisner,Zbigniew Transform callback run asynchronously: csv() .from('82,Preisner,Zbigniew\n94,Gainsbourg,Serge') .to(console.log) .transform(function(row, index, callback){ process.nextTick(function(){ callback(null, row.reverse()); }); }); // Executing `node samples/transform.js`, print: // 94,Gainsbourg,Serge\n82,Preisner,Zbigniew Transform callback returning a string: csv() .from('82,Preisner,Zbigniew\n94,Gainsbourg,Serge') .to(console.log) .transform(function(row, index){ return (index>0 ? ',' : '') + row[0] + ":" + row[2] + ' ' + row[1]; }); // Executing `node samples/transform.js`, print: // 82:Zbigniew Preisner,94:Serge Gainsbourg */ Transformer = function(csv) { this.csv = csv; this.running = 0; this.options = { parallel: 100 }; this.todo = []; return this; }; Transformer.prototype.__proto__ = stream.prototype; /* no doc `headers()` ---------------------------- Print headers. */ Transformer.prototype.headers = function() { var k, label, labels; labels = this.csv.options.to.columns || this.csv.options.from.columns; if (typeof labels === 'object') { labels = (function() { var _results; _results = []; for (k in labels) { label = labels[k]; _results.push(label); } return _results; })(); } return this.csv.stringifier.write(labels); }; /* no doc `write(line)` ---------------------------------- Call a callback to transform a line. Called for each line after being parsed. It is responsible for transforming the data and finally calling `write`. */ Transformer.prototype.write = function(line) { var column, columns, csv, done, finish, i, lineAsObject, run, self, sync, _i, _j, _len, _len1; self = this; csv = this.csv; if (this.columns == null) { columns = csv.options.from.columns; if (typeof columns === 'object' && columns !== null && !Array.isArray(columns)) { columns = Object.keys(columns); } if (csv.state.count === 0 && columns === true) { columns = csv.options.from.columns = line; return; } this.columns = columns != null ? columns : false; } else { columns = this.columns; } if (columns) { if (Array.isArray(line)) { lineAsObject = {}; for (i = _i = 0, _len = columns.length; _i < _len; i = ++_i) { column = columns[i]; lineAsObject[column] = line[i] != null ? line[i] : null; } line = lineAsObject; } else { lineAsObject = {}; for (i = _j = 0, _len1 = columns.length; _j < _len1; i = ++_j) { column = columns[i]; lineAsObject[column] = line[column] != null ? line[column] : null; } line = lineAsObject; } } finish = function(line) { if (csv.options.to.header === true && (csv.state.count - self.running) === 1) { self.headers(); } csv.stringifier.write(line); line = self.todo.shift(); if (line) { return run(line); } if (csv.state.transforming === 0 && self.closed === true) { return self.emit('end', csv.state.count); } }; csv.state.count++; if (!this.callback) { return finish(line); } sync = this.callback.length !== 3; csv.state.transforming++; self = this; done = function(err, line) { var isObject; self.running--; if (err) { return csv.error(err); } isObject = typeof line === 'object' && !Array.isArray(line); if (isObject && csv.options.to.newColumns && !csv.options.to.columns) { Object.keys(line).filter(function(column) { return self.columns.indexOf(column) === -1; }).forEach(function(column) { return self.columns.push(column); }); } csv.state.transforming--; return finish(line); }; run = function(line) { var err; self.running++; try { if (sync) { return done(null, self.callback(line, csv.state.count - 1)); } else { return self.callback(line, csv.state.count - 1, done); } } catch (_error) { err = _error; return done(err); } }; if (this.running === this.options.parallel) { this.todo.push(line); return false; } run(line); return true; }; /* no doc `end()` ------------------------ A transformer instance extends the EventEmitter and emit the 'end' event when the last callback is called. */ Transformer.prototype.end = function() { if (this.closed) { return this.csv.error(new Error('Transformer already closed')); } this.closed = true; if (this.csv.state.transforming === 0) { return this.emit('end'); } }; module.exports = function(csv) { return new Transformer(csv); }; module.exports.Transformer = Transformer;