UNPKG

@thi.ng/csv

Version:

Customizable, transducer-based CSV parser/object mapper and transformer

56 lines (55 loc) 1.85 kB
import { isArray } from "@thi.ng/checks/is-array"; import { isIterable } from "@thi.ng/checks/is-iterable"; import { wrap } from "@thi.ng/strings/wrap"; import { compR } from "@thi.ng/transducers/compr"; import { iterator } from "@thi.ng/transducers/iterator"; import { isReduced } from "@thi.ng/transducers/reduced"; import { str } from "@thi.ng/transducers/str"; import { transduce } from "@thi.ng/transducers/transduce"; function formatCSV(opts = {}, src) { return isIterable(src) ? iterator(formatCSV(opts), src) : (rfn) => { let { cols = [], delim = ",", quote = '"', header } = opts; let colTx; const reQuote = new RegExp(quote, "g"); const reduce = rfn[2]; let headerDone = false; return compR(rfn, (acc, row) => { if (!headerDone) { if (!header && !isArray(row)) { header = Object.keys(row); } colTx = isArray(cols) ? cols : header ? header.map( (id) => cols[id] ) : []; } const $row = isArray(row) ? row : header.map((k) => row[k]); const line = (header || $row).map((_, i) => { const val = $row[i]; const cell = val != null ? colTx[i] ? colTx[i](val) : String(val) : ""; return cell.indexOf(quote) !== -1 ? wrap(quote)( cell.replace( reQuote, `${quote}${quote}` ) ) : cell; }).join(delim); if (!headerDone) { if (header) { acc = reduce(acc, header.join(delim)); } else { header = $row; } headerDone = true; !isReduced(acc) && (acc = reduce(acc, line)); return acc; } else { return reduce(acc, line); } }); }; } const formatCSVString = (opts = {}, src) => transduce(formatCSV(opts), str(opts.rowDelim || "\n"), src); export { formatCSV, formatCSVString };