UNPKG

simplecsv

Version:

Fast and compact CSV parser for parsing CSV with CSV to JSON support.

282 lines (262 loc) 8.56 kB
/** * @copyright Copyright (c) 2015 All Rights Reserved. * @author Baris Yuksel <baris@onehundredyearsofcode.com> * * @file Module that exports the csv() class. Should be used * with new() to create new parser classes. */ exports = module.exports = csv; var csvdata = require('./csvdata.js'), private = require ('./private.js'); /** * Creates a new csv parser. * @constructor */ function csv() {} /** * Converts CSV string to JSON. * @param {string} input - csv string * @param {dictionary} argdic - arguments for parsing the csv string * @return {string} JSON represantion of the csv string. * @example * CSVToJSON("magician, born\r\nhoudini, 1874\r\ncopperfield, 1956\r\n", * {hasHeaders: true}); * // outputs: * [ { "magician":"houdini", * "born": 1874 }, * { "magician":"copperfield", * "born":1956 } ] */ csv.prototype.CSVToJSON = function(input, argdic) { return this.csvdataToJSON(this.parseString(input, argdic)); }; /** * Converts JSON to CSV string. * @param {string} input - JSON table string * @param {dictionary} argdic - arguments for creating the csv string. * @return {string} csv string corresponding to the JSON input. * @example * JSONToCSV( [ { "magician":"houdini", "born": 1874 }, * { "magician":"copperfield", "born":1956 } ]); * // outputs: * "magician, born\r\nhoudini, 1874\r\ncopperfield, 1956\r\n", */ csv.prototype.JSONToCSV = function(input, argdic) { return this.csvdataToString(this.JSONToCsvdata(input), argdic); }; /** * Produces simple string output of csvdata. * @param {string} input - a csvdata object. * @param {dictionary} argdic - arguments for creating the csv string. * @return {string} csv string corresponding to the csvdata object. * @example * // Given csvdata of: * { column_names: { "magician", "born"}, * rows: { { "houdini", 1874 }, * { "copperfield", 1956} } * } * csvdataToString() // will produce * "magician, born\r\nhoudini, 1874\r\ncopperfield, 1956\r\n" */ csv.prototype.csvdataToString = function(input, argdic) { var delim = private.getArg(argdic, 'delim', ','); var str = ''; var columnnames = input.columnNames; if (typeof columnnames !== 'undefined') { for (var i = 0; i < columnnames.length; i++) { str += private.doubleQuoteIfNecessary(columnnames[i]) + delim; } if (str.length > 0) { str = str.slice(0, - 1) + '\r\n'; } } var rows = input.rows; if (typeof rows === 'undefined') return str; for (var k = 0; k < rows.length; k++) { var temp = ''; for (var m = 0; m < rows[k].length; m++) { temp += private.doubleQuoteIfNecessary(rows[k][m]) + delim; } if (temp.length > 0) { temp = temp.slice(0, - 1); } str += temp + '\r\n'; } return str; }; /** * Produces the JSON of the csvdata. * Uses JSON table schema: {@link http://dataprotocols.org/json-table-schema/} * @param {string} input - a csvdata object. * @return {string} JSON represantion of the csvdata object. * @example * // Given csvdata of: * { column_names: { "magician", "born"}, * rows: { { "houdini", 1874 }, * { "copperfield", 1956} } * } * * csvdataToJSON() // will produce: * [ { "magician":"houdini", * "born": 1874 }, * { "magician":"copperfield", * "born":1956 } ] */ csv.prototype.csvdataToJSON = function(input) { var columnnames = input.columnNames; // No columnnames, let's fill it up with generated names if ((typeof columnnames === 'undefined' || columnnames.length === 0) && input.columnCount !== 0) { var digitCount = input.columnCount.toString().length; columnnames = []; // Don't modify input's columnnames, assign a new array for (var m = 0; m < input.columnCount; m++) { var neededZeroCount = digitCount - m.toString().length; var fullname = 'Col ' + Array(neededZeroCount + 1).join('0') + m.toString(); columnnames.push(fullname); } } var rows = input.rows; var output = []; for (var i = 0; i < rows.length; i++) { var entry = {}; for (var j = 0; j < rows[i].length; j++) { entry[columnnames[j]] = rows[i][j]; } output.push(entry); } return JSON.stringify(output); }; /** * Produces the csvdata of table formatted JSON * Uses JSON table schema: {@link http://dataprotocols.org/json-table-schema/} * @param {string} - JSON table. * @return {csvdata} the csvdata object representing the JSON input. * @example * // Given JSON of: * { "magician":"houdini", * "born": 1874 }, * { "magician":"copperfield", * "born":1956 } * // will produce * { column_names: { "magician", "born"}, * rows: { { "houdini", 1874 }, * { "copperfield", 1956} } * } */ csv.prototype.JSONToCsvdata = function(jsonStr) { var obj = JSON.parse(jsonStr); var rows = []; for (var i = 0; i < obj.length; i++) { var line = []; for (var cell in obj[i]) { if (obj[i].hasOwnProperty(cell)) { line.push(obj[i][cell]); } } rows.push(line); } var columnnames = []; for (var attr in obj[0]) { if (obj[0].hasOwnProperty(attr)) { columnnames.push(attr); } } var output = { rows: rows, columnNames: columnnames, rowCount: rows.length, columnCount: columnnames.length }; return this.makeCsvdataFromObj(output); }; /** * Produces the csvdata of csv string. * @param {string} input - csv string * @param {dictionary} argdic - arguments for parsing the csv string * @return {csvdata} the csvdata object representing the csv string. * @example * parseString("magician, born\r\nhoudini, 1874\r\ncopperfield, 1956\r\n", * {hasHeaders: true}); * // will produce * { column_names: { "magician", "born"}, * rows: { { "houdini", 1874 }, * { "copperfield", 1956} } * } */ csv.prototype.parseString= function(str, argdic) { var parsed = private.parseStringToArray(str, argdic); var retValue = new csvdata(); var hasHeaders = private.getArg(argdic, 'hasHeaders', false); if (hasHeaders) { retValue.columnNames = parsed.shift(); if (typeof retValue.columnNames !== 'undefined') { retValue.columnCount = retValue.columnNames.length; } } retValue.rows = parsed; retValue.rowCount = parsed.length; if (retValue.columnCount === 0 && retValue.rowCount > 0) { retValue.columnCount = retValue.rows[0].length; } return retValue; }; /** * Can take an object and if the object has the same properties * as csvdata, it creates a csvdata from those properties by deep copying. * @param {object} - obj Any JS object with csvdata like properties. * @return {csvdata} a new csvdata object deep copied from obj. */ csv.prototype.makeCsvdataFromObj= function(obj) { if (typeof obj === 'undefined') return; var retVal = new csvdata(); if (typeof obj.columnCount !== 'undefined') { retVal.columnCount = obj.columnCount; } if (typeof obj.columnNames !== 'undefined') { retVal.columnNames = obj.columnNames.slice(); } if (typeof obj.rows !== 'undefined') { retVal.rows = []; for (var i = 0; i < obj.rows.length; i++) { retVal.rows.push(obj.rows[i].slice()); } } if (typeof obj.columnCount !== 'undefined') { retVal.rowCount = obj.rowCount; } return retVal; }; /** * Find errors. * @param {csvdata} - input a csvdata object. * @return {Array<string>} The errors in the csvdata object. */ csv.prototype.findErrors= function(input) { // Check length of rows var errors = []; var rows = input.rows; var columncount = rows[0].length; var types = []; for (var j = 0; j < rows[0].length; j++) { types.push(typeof rows[0][j]); } for (var r_index = 0; r_index < rows.length; r_index++) { for (var c_index = 0; c_index < rows[r_index].length; c_index++) { var actualtype = typeof rows[r_index][c_index]; if ( actualtype !== types[c_index]) { errors.push('Type mismatch at row:' + r_index + ' col:' + c_index + ' expected:' + types[c_index] + ' actual:' + actualtype); } } } if (input.columnCount !== columncount) { errors.push('Column count is ' + input.columnCount + ' but Row 0 has ' + columncount + ' cols'); } for (var i = 0; i < rows.length; i++) { if (rows[i].length !== columncount) { errors.push('Row ' + i + ' has ' + rows[i].length + ' cols, Row 0 has ' + columncount); } } return errors; };