Skip to content

Commit

Permalink
fix: Performance optimizations (#491)
Browse files Browse the repository at this point in the history
* perf: use native flatMap instead of custom implementation

* perf: use native join instead of custom implementation

* chore: drop support for node 10 and add tests for node 14 and 15
  • Loading branch information
juanjoDiaz authored Nov 1, 2020
1 parent 4c74c1e commit 471f5a7
Show file tree
Hide file tree
Showing 6 changed files with 19 additions and 52 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
language: node_js
node_js:
- "10"
- "11"
- "12"
- "13"
- "14"
- "15"
script:
- "npm run lint"
- "npm run test-with-coverage"
Expand Down
3 changes: 1 addition & 2 deletions lib/JSON2CSVAsyncParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

const { Transform } = require('stream');
const JSON2CSVTransform = require('./JSON2CSVTransform');
const { fastJoin } = require('./utils');

class JSON2CSVAsyncParser {
constructor(opts, transformOpts) {
Expand Down Expand Up @@ -51,7 +50,7 @@ class JSON2CSVAsyncParser {
let csvBuffer = [];
this.processor
.on('data', chunk => csvBuffer.push(chunk.toString()))
.on('finish', () => resolve(fastJoin(csvBuffer, '')))
.on('finish', () => resolve(csvBuffer.join('')))
.on('error', err => reject(err));
});
}
Expand Down
16 changes: 6 additions & 10 deletions lib/JSON2CSVBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const os = require('os');
const lodashGet = require('lodash.get');
const { getProp, fastJoin, flattenReducer } = require('./utils');
const { getProp } = require('./utils');
const defaultFormatter = require('./formatters/default');
const numberFormatterCtor = require('./formatters/number')
const stringFormatterCtor = require('./formatters/string');
Expand Down Expand Up @@ -111,10 +111,9 @@ class JSON2CSVBase {
* @returns {String} titles as a string
*/
getHeader() {
return fastJoin(
this.opts.fields.map(fieldInfo => this.opts.formatters.header(fieldInfo.label)),
this.opts.delimiter
);
return this.opts.fields
.map(fieldInfo => this.opts.formatters.header(fieldInfo.label))
.join(this.opts.delimiter);
}

/**
Expand All @@ -123,7 +122,7 @@ class JSON2CSVBase {
*/
preprocessRow(row) {
return this.opts.transforms.reduce((rows, transform) =>
rows.map(row => transform(row)).reduce(flattenReducer, []),
rows.flatMap(row => transform(row)),
[row]
);
}
Expand All @@ -145,10 +144,7 @@ class JSON2CSVBase {
return undefined;
}

return fastJoin(
processedRow,
this.opts.delimiter
);
return processedRow.join(this.opts.delimiter);
}

/**
Expand Down
12 changes: 5 additions & 7 deletions lib/JSON2CSVParser.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const JSON2CSVBase = require('./JSON2CSVBase');
const { fastJoin, flattenReducer } = require('./utils');

class JSON2CSVParser extends JSON2CSVBase {
constructor(opts) {
Expand Down Expand Up @@ -60,8 +59,7 @@ class JSON2CSVParser extends JSON2CSVBase {
if (this.opts.transforms.length === 0) return processedData;

return processedData
.map(row => this.preprocessRow(row))
.reduce(flattenReducer, []);
.flatMap(row => this.preprocessRow(row));
}

/**
Expand All @@ -71,10 +69,10 @@ class JSON2CSVParser extends JSON2CSVBase {
* @returns {String} CSV string (body)
*/
processData(data) {
return fastJoin(
data.map(row => this.processRow(row)).filter(row => row), // Filter empty rows
this.opts.eol
);
return data
.map(row => this.processRow(row))
.filter(row => row) // Filter empty rows
.join(this.opts.eol);
}
}

Expand Down
10 changes: 4 additions & 6 deletions lib/transforms/unwind.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

const lodashGet = require('lodash.get');
const { setProp, flattenReducer } = require('../utils');
const { setProp } = require('../utils');

function getUnwindablePaths(obj, currentPath) {
return Object.keys(obj).reduce((unwindablePaths, key) => {
Expand All @@ -16,8 +16,7 @@ function getUnwindablePaths(obj, currentPath) {
} else if (Array.isArray(value)) {
unwindablePaths.push(newPath);
unwindablePaths = unwindablePaths.concat(value
.map(arrObj => getUnwindablePaths(arrObj, newPath))
.reduce(flattenReducer, [])
.flatMap(arrObj => getUnwindablePaths(arrObj, newPath))
.filter((item, index, arr) => arr.indexOf(item) !== index));
}

Expand All @@ -34,7 +33,7 @@ function getUnwindablePaths(obj, currentPath) {
function unwind({ paths = undefined, blankOut = false } = {}) {
function unwindReducer(rows, unwindPath) {
return rows
.map(row => {
.flatMap(row => {
const unwindArray = lodashGet(row, unwindPath);

if (!Array.isArray(unwindArray)) {
Expand All @@ -52,8 +51,7 @@ function unwind({ paths = undefined, blankOut = false } = {}) {

return setProp(clonedRow, unwindPath, unwindRow);
});
})
.reduce(flattenReducer, []);
});
}

paths = Array.isArray(paths) ? paths : (paths ? [paths] : undefined);
Expand Down
27 changes: 1 addition & 26 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,7 @@ function setProp(obj, path, value) {
return Object.assign({}, obj, { [key]: newValue });
}

function flattenReducer(acc, arr) {
try {
// This is faster but susceptible to `RangeError: Maximum call stack size exceeded`
acc.push(...arr);
return acc;
} catch (err) {
// Fallback to a slower but safer option
return acc.concat(arr);
}
}

function fastJoin(arr, separator) {
let isFirst = true;
return arr.reduce((acc, elem) => {
if (isFirst) {
isFirst = false;
return `${elem}`;
}

return `${acc}${separator}${elem}`;
}, '');
}

module.exports = {
getProp,
setProp,
fastJoin,
flattenReducer
setProp
};

0 comments on commit 471f5a7

Please sign in to comment.