UNPKG

@naturalcycles/nodejs-lib

Version:
57 lines (56 loc) 1.82 kB
// 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]; }