@naturalcycles/nodejs-lib
Version:
Standard library for Node.js
57 lines (56 loc) • 1.82 kB
JavaScript
// Inspired by: https://github.com/ryu1kn/csv-writer/
import { _assert } from '@naturalcycles/js-lib/error/assert.js';
export class CSVWriter {
constructor(cfg) {
this.cfg = {
delimiter: ',',
includeHeader: true,
...cfg,
};
}
cfg;
writeRows(rows) {
let s = '';
// Detect columns based on content, if not defined upfront
this.cfg.columns ||= arrayToCSVColumns(rows);
if (this.cfg.includeHeader && rows.length) {
s += this.writeHeader() + '\n';
}
return s + rows.map(row => this.writeRow(row)).join('\n');
}
writeHeader() {
_assert(this.cfg.columns, 'CSVWriter cannot writeHeader, because columns were not provided');
return this.cfg.columns.map(col => this.quoteIfNeeded(col)).join(this.cfg.delimiter);
}
writeRow(row) {
_assert(this.cfg.columns, 'CSVWriter cannot writeRow, because columns were not provided');
return this.cfg.columns
.map(col => this.quoteIfNeeded(String(row[col] ?? '')))
.join(this.cfg.delimiter);
}
quoteIfNeeded(s) {
return this.shouldQuote(s) ? this.quote(s) : s;
}
quote(s) {
return `"${s.replaceAll('"', '""')}"`;
}
shouldQuote(s) {
return s.includes(this.cfg.delimiter) || s.includes('"') || s.includes('\n') || s.includes('\r');
}
}
export function arrayToCSVString(arr, cfg = {}) {
const writer = new CSVWriter(cfg);
return writer.writeRows(arr);
}
/**
* Iterates over the whole array and notes all possible columns.
*/
export function arrayToCSVColumns(arr) {
const cols = new Set();
for (const row of arr) {
for (const col of Object.keys(row)) {
cols.add(col);
}
}
return [...cols];
}