json2csv
Version:
Convert JSON to CSV
60 lines (50 loc) • 1.99 kB
JavaScript
const lodashGet = require('lodash.get');
const { setProp, unsetProp } = require('../utils');
function getUnwindablePaths(obj, currentPath) {
return Object.keys(obj).reduce((unwindablePaths, key) => {
const newPath = currentPath ? `${currentPath}.${key}` : key;
const value = obj[key];
if (typeof value === 'object'
&& value !== null
&& !Array.isArray(value)
&& Object.prototype.toString.call(value.toJSON) !== '[object Function]'
&& Object.keys(value).length) {
unwindablePaths = unwindablePaths.concat(getUnwindablePaths(value, newPath));
} else if (Array.isArray(value)) {
unwindablePaths.push(newPath);
unwindablePaths = unwindablePaths.concat(value
.flatMap(arrObj => getUnwindablePaths(arrObj, newPath))
.filter((item, index, arr) => arr.indexOf(item) !== index));
}
return unwindablePaths;
}, []);
}
/**
* Performs the unwind recursively in specified sequence
*
* @param {String[]} unwindPaths The paths as strings to be used to deconstruct the array
* @returns {Object => Array} Array of objects containing all rows after unwind of chosen paths
*/
function unwind({ paths = undefined, blankOut = false } = {}) {
function unwindReducer(rows, unwindPath) {
return rows
.flatMap(row => {
const unwindArray = lodashGet(row, unwindPath);
if (!Array.isArray(unwindArray)) {
return row;
}
if (!unwindArray.length) {
return unsetProp(row, unwindPath);
}
const baseNewRow = blankOut ? {} : row;
const [firstRow, ...restRows] = unwindArray;
return [
setProp(row, unwindPath, firstRow),
...restRows.map(unwindRow => setProp(baseNewRow, unwindPath, unwindRow))
];
});
}
paths = Array.isArray(paths) ? paths : (paths ? [paths] : undefined);
return dataRow => (paths || getUnwindablePaths(dataRow)).reduce(unwindReducer, [dataRow]);
}
module.exports = unwind;