Search code examples
javascriptnode.jsjson2csv

How to parse JS Object containing arrays to use in json2csv module in JavaScript?


I have an array(arr) of objects where each object looks like this:-

{
   "name" : "someName",
   "address" : "someAddress",
   "attributes" : [
      {
        "attName" : "Sea",
      },
      {
        "attName" : "Beach",
      }
    ],
   "values" : [ 
      "2540",
      "3345",
      "2340"
    ]
}

The arrays "values" and "attributes" may have different number of elements for different objects.

Now when I convert this array of objects to csv using this code:

const json2csv = require('json2csv').parse;
const csvString = json2csv(arr);

I get the following output:

name, address, attributes, values
"someName","someAddress", [{"attName" : "Sea"},{"attName" : "Beach"}], ["2540", "3345", "2340"]

However, I want my csv something like this:

name, address, attName, attName, value, value, value
"someName","someAddress", "Sea", "Beach", "2540", "3345", "2340"

I know that JSON doesn't allow multiple keys with same name but I can't figure out how to achieve this.


Solution

  • Expanding an array column into multiple column of the same title is non-standard.

    However, if you really wants to do that, you can write a custom function to convert the object array into a 2D string array. Then use a library to convert the 2D string array into csv.

    For example:

    // import a library to convert 2d string array into csv string
    let { to_csv } = require("@beenotung/tslib/csv");
    
    // the sample object array
    let records = [{
            "name": "someName",
            "address": "someAddress",
            "attributes": [
                {
                    "attName": "Sea",
                },
                {
                    "attName": "Beach",
                }
            ],
            "values": [
                "2540",
                "3345",
                "2340"
            ]
        }];
    
    let csv = object_array_to_csv(records)
    console.log(csv);
    /* will print out below lines:
    name,address,attName,attName,values,values,values
    someName,someAddress,Sea,Beach,2540,3345,2340
    */
    
    // convert an object array into csv text
    function object_array_to_csv(objects) {
        let records = objects.map(object_to_opt_list);
        let titles = records.find(record => record.length > 0)
            .map(field => field.key);
        let values = records.map(record => record.map(field => field.value));
        let rows = [titles, ...values];
        let csv = to_csv(rows);
        return csv;
    }
    
    // convert an object with string and array of string/object into 2d key-value array
    function object_to_opt_list(object) {
        let opt_list = [];
        for (const [key, value] of Object.entries(object)) {
            if (Array.isArray(value)) {
                opt_list.push(...value_to_opt_list(key, value));
            }
            else {
                opt_list.push({ key, value });
            }
        }
        return opt_list;
    }
    
    // convert recursively expand the array/object value into 2d key-value array
    function value_to_opt_list(key, value) {
        if (Array.isArray(value)) {
            return value.map(value => value_to_opt_list(key, value))
                .reduce((acc, c) => acc.concat(c));
        }
        if (typeof value === "object") {
            return object_to_opt_list(value);
        }
        return [{ key, value }];
    }