logo

How to create a CSV using template strings in Node.js

I recently had to create a CSV from some data returned by the Jira API. Went on the Internets to find a module on NPM that would do the job. Unfortunately, I wasn’t able to find a single module that would perform only that simple task.

We were already using a module to generate Excel exports, inside the application, but it was generating XLSX files. It also looked like it has a funky license, so I decided to drop it altogether.

The client asked for a CSV, so I set out to write my own thing. It’s nothing fantastic. Actually, the implementation is very naive, but it does the job, and it does it well. As long as you pass data that is formatted correctly, it just works.

The main file has just a few lines of code:

// csv.js
import trueTypeOf from './true-type-of';

export const createCSV = (headers, dataset) => {
	if (!Array.isArray(headers)) {
    throw new TypeError(`Expected an array of table headers but got ${trueTypeOf(headers)}`);
  }

  if (!Array.isArray(dataset)) {
    throw new TypeError(`Expected an array of dataset items but got ${trueTypeOf(dataset)}`);
  }

  if (dataset[0] && dataset[0].length !== headers.length) {
    throw new RangeError(`Dataset row length is different than headers row length: got ${headers.length} headers and ${dataset[0].length} items`);
  }

  return `${headers}\n${dataset.reduce((acc, curr) => acc.concat(`${curr.join(',').trim()}\n`),'')}`.trim();
}

As you can see, there is more error prevention logic than actual code to generate the CSV.

To give you a brief explanation of the code, the createCSV function needs two arrays — the array of document headers and a multi-dimensional array of values. The catch is that each row of the multi-dimensional array should have the same length as the headers array. This is where the naive implementation appears, since I’m only checking whether the first row has the proper length.

Now, the trueTypeOf function is a trick I learned a long time ago, for getting the proper type — parent object — of any valid JavaScript identifier.

Here is how the function looks, in its own module:

// true-type-of.js

export const trueTypeOf = input => Object.prototype.toString.call(input)
	.replace(/(\[object |\])/ig, '')
	.toLowerCase();

It basically calls Object’s toString() method and it passes the input to it. The output of calling it is [object Type]. I don’t want to confuse my “users” when I throw an error, so all I’m doing after calling Object.prototype.toString is removing the square brackets and the “object” string from my final output and then lowercasing what is left.

Here is some sample output of the function:

> _trueTypeOf(null)
'null'
> _trueTypeOf(undefined)
'undefined'
> _trueTypeOf(Infinity)
'number'
> _trueTypeOf('')
'string'
> _trueTypeOf(true)
'boolean'
> _trueTypeOf({})
'object'
> _trueTypeOf([])
'array'
> _trueTypeOf(/a/)
'regexp'
> _trueTypeOf(new Date())
'date'

This is it! Go ahead and use it in your own code. This is also published as a module on NPM.

Enjoy!

References

Copyright (c) 2023 Adrian Oprea. All rights reserved.