@thi.ng/csv
Version:
Customizable, transducer-based CSV parser/object mapper and transformer
56 lines (55 loc) • 1.85 kB
JavaScript
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
};