zemirco / json2csv

Convert json to csv with column titles
http://zemirco.github.io/json2csv
MIT License
2.71k stars 365 forks source link

New Rows In CSV #293

Closed isaac-martin closed 6 years ago

isaac-martin commented 6 years ago

Is it possible to force new rows? Have a format I need to match that has each object over three rows.

Everything I have tried is being stripped out when parsed.

juanjoDiaz commented 6 years ago

Can you elaborate a bit more? I'm not sure of what are you trying to achieve :)

isaac-martin commented 6 years ago

Sorry! I am getting data into a legacy manufacturing system and need to follow a very particular set of rules. TSV saved as .txt so all achievable with json2csv Let's use the below code as an example - mine will be more complex but for the sake of simplicity we can use this.

const Json2csvParser = require('json2csv').Parser;
const fields = ['car', 'price', 'color'];
const myCars = [
  {
  "car": "Audi",
  "price": 40000,
  "color": "blue",
  "location_city" : "New York",
  "location_state" : "New York",
  "location_country" : "United States",
  },  {
  "car": "BMW",
  "price": 50000,
  "color": "red",
  "location_city" : "Raleigh",
  "location_state" : "North Carolina",
  "location_country" : "United States",
  },
];
const json2csvParser = new Json2csvParser({ fields ,  header: false});
const csv = json2csvParser.parse(myCars);
console.log(csv);

Would output the following to console

"Audi", 40000, "blue", "New York", "New York", "United States"
"BMW", 50000, "red", "Raleigh", "North Carolina", "United States"

What I would want to achieve would be something like the below.

"Audi", 40000, "blue",
"New York", "New York", "United States" 
"BMW", 50000, "red",
"Raleigh", "North Carolina", "United States"
juanjoDiaz commented 6 years ago

I see. Thanks for the code example. I assume that you are actually not using the fields field so ti matches your expected output.

I think that's very specific requirement (and a bit odd one if you allow me).

I don't think that is something that should be inside json2csv since it's not generic enough. But I think that's something that you can easily achieve in your code with some data preprocessing.

const Json2csvParser = require('json2csv').Parser;
const row1fields = ['car', 'price', 'color'];
const row2fields = ['location_city', 'location_state', 'location_country'];
const myCars = [
  {
  "car": "Audi",
  "price": 40000,
  "color": "blue",
  "location_city" : "New York",
  "location_state" : "New York",
  "location_country" : "United States",
  },  {
  "car": "BMW",
  "price": 50000,
  "color": "red",
  "location_city" : "Raleigh",
  "location_state" : "North Carolina",
  "location_country" : "United States",
  },
];

const getObjectProps = (obj, props) => props.reduce((acc, prop, i) => {
  acc[i] = obj[prop]; // Use just the index so json2csv treats them as the same colum
  return acc;
}, {});

// If you use ES7
// const getObjectProps = (obj, props) => props.reduce((acc, prop, i) => { ...acc, i: obj[prop] });

const processedData = myCars.reduce((acc, row) => {
  acc.push(getObjectProps(row, row1fields));
  acc.push(getObjectProps(row, row2fields));
}, []);

const json2csvParser = new Json2csvParser({ header: false});
const csv = json2csvParser.parse(processedData);
console.log(csv);

That's just an example I wrote in 2 seconds, there might be mistakes since I didn't run it. And you should adapt it to your coding style.

Does that help?

isaac-martin commented 6 years ago

Perfect, Thanks that will at least steer me in the right direction. Have a nice weekend!

juanjoDiaz commented 6 years ago

You too!

I'll close this. Feel free to re-open or just ask if you have any more questions :)

isaac-martin commented 6 years ago

Still couldn't figure it out @juanjoDiaz - returns a ReferenceError that acc is not defined inside the getObjectProps function. Understand if you're too busy to help out, this is a little out of my skill level and as you said quite an unusual requirement.

juanjoDiaz commented 6 years ago

My bad. I told you it wasn't tested. :p

The right function is:

const processedData = myCars.reduce((acc, row) => {
  acc.push(getObjectProps(row, row1fields));
  acc.push(getObjectProps(row, row2fields));
  return acc;
}, []);

Just tested the whole thing and it works :)

Just let me know if you have any other problem :)

isaac-martin commented 6 years ago

So I thought I had this all working until i realized the JSON we are getting can sometimes have nested content inside.

Data would look like this as an example

const myCars = [
  {
  "car": "Audi",
  "price": 40000,
  "color": "blue",
  "location_city" : "New York",
  "location_state" : "New York",
  "location_country" : "United States",
"lineitems": [
            {
                "prod_qty": 1,
                "prod_sku": "colorfulpants",
                "prod_price": "140.00"
            },
            {
                "prod_qty": 1,
                "prod_sku": "blackdress",
                "prod_price": "0.00"
            }
        ],
  },  {
  "car": "BMW",
  "price": 50000,
  "color": "red",
  "location_city" : "Raleigh",
  "location_state" : "North Carolina",
  "location_country" : "United States",
"lineitems": [
            {
                "prod_qty": 2,
                "prod_sku": "party",
                "prod_price": "100.00"
            },
            {
                "prod_qty": 1,
                "prod_sku": "sweater",
                "prod_price": "0.00"
            }
        ],
  },
];

Ideally I need an output like the below - where we are making a new row for each item inside lineitems

"Audi", 40000, "blue",
"New York", "New York", "United States" 
"1","colorfulpants","140.00"
"1","blackdress","0.00"
"BMW", 50000, "red",
"Raleigh", "North Carolina", "United States"
"2","party","100.00"
"1","sweater","0"

I've been playing around for a few days but I am not sure this is even going to be possible?

juanjoDiaz commented 6 years ago
const Json2csvParser = require('json2csv').Parser;
const row1fields = ['car', 'price', 'color'];
const row2fields = ['location_city', 'location_state', 'location_country'];
const rowNestedField = 'lineitems';
const nestedObjectsFields = ['prod_qty', 'prod_sku', 'prod_price'];
const myCars = [
  {
  "car": "Audi",
  "price": 40000,
  "color": "blue",
  "location_city" : "New York",
  "location_state" : "New York",
  "location_country" : "United States",
"lineitems": [
            {
                "prod_qty": 1,
                "prod_sku": "colorfulpants",
                "prod_price": "140.00"
            },
            {
                "prod_qty": 1,
                "prod_sku": "blackdress",
                "prod_price": "0.00"
            }
        ],
  },  {
  "car": "BMW",
  "price": 50000,
  "color": "red",
  "location_city" : "Raleigh",
  "location_state" : "North Carolina",
  "location_country" : "United States",
"lineitems": [
            {
                "prod_qty": 2,
                "prod_sku": "party",
                "prod_price": "100.00"
            },
            {
                "prod_qty": 1,
                "prod_sku": "sweater",
                "prod_price": "0.00"
            }
        ],
  },
];

const getObjectProps = (obj, props) => props.reduce((acc, prop, i) => {
  acc[i] = obj[prop]; // Use just the index so json2csv treats them as the same colum
  return acc;
}, {});
const getNestedContent = (row, prop, nestedProps) => row[prop]
  .map(obj => getObjectProps(obj, nestedProps));

const processedData = myCars.reduce((acc, row) => {
  acc.push(getObjectProps(row, row1fields));
  acc.push(getObjectProps(row, row2fields));
  acc = acc.concat(getNestedContent(row, rowNestedField, nestedObjectsFields))
  return acc;
}, []);

const json2csvParser = new Json2csvParser({ header: false});
const csv = json2csvParser.parse(processedData);
console.log(csv);

I expect 10% of your paycheck at the end of the month :p

JK, hope that helps! The "prod_qty" property is not quoted in the result because it's a number and not a string. You can change it also with a bit of preprocessing.