2

I am getting a below structure after converting a CSV file to a JS object

"category,name,includeInAuto,srNumber,testType
"Test1","Name1","true",1,"type1"
"Test2","Name2","true",1,"type2"
"Test3","Name3","true",1,"type3"
"Test4","Name4","true",1,"type4"
"Test5","Name5","true",1,"type5"
"Test6","Name6","true",1,"type6"
"Test7","Name7","true",1,"type7"

And I am trying to convert it like below

[{"category": "Test1", "name": "Name1", "includeInAuto": "true", "srNumber": 1 "testType": "type1"},
 {"category": "Test2", "name": "Name2", "includeInAuto": "true", "srNumber": 2 "testType": "type2"},
 {"category": "Test3", "name": "Name3", "includeInAuto": "true", "srNumber": 3 "testType": "type3"},
 {"category": "Test4", "name": "Name4", "includeInAuto": "true", "srNumber": 4 "testType": "type4"},
 {"category": "Test5", "name": "Name5", "includeInAuto": "true", "srNumber": 5 "testType": "type5"},
 {"category": "Test6", "name": "Name6", "includeInAuto": "true", "srNumber": 6 "testType": "type6"},
 {"category": "Test7", "name": "Name7", "includeInAuto": "true", "srNumber": 7 "testType": "type7"}]

I have tried using map like Object.entries(obj); or Object.keys(obj); or converting it an array first Array.from(obj) but not getting an expected result.

above all approaches separates each word to a single character like category to "c","a","t","e","g","o","r","y"

Can someone please help me to achieve the same?

UPDATE

if I edit the csv file in excel and then try to parse it then I get a below structure where instead of surrounding all the values with double quotes whole data is surrounded in double quotes as below

"category,name,includeInAuto,srNumber,testType 
Test1,Name1,true,1,type1 
Test2,Name2,true,2,type2
Test3,Name3,true,3,type3
Test4,Name4,true,4,type4
Test5,Name5,true,5,type5
Test6,Name6,true,6,type6
Test7,Name7,true,7,type7"

if instead of above any of the value has any special character in it lets assume if I change name7 to name,7 then FileReader return below structure

"category,name,includeInAuto,srNumber,testType 
    Test1,Name1,true,1,type1 
    Test2,Name2,true,2,type2
    Test3,Name3,true,3,type3
    Test4,Name4,true,4,type4
    Test5,Name5,true,5,type5
    Test6,Name6,true,6,type6
    Test7,\"Name,7\",true,6,type6"

in above whole csv string is in double quotes but the name name, 7 is also in double quotes with some extra slashes now instead of 4 comma separated values we have 5 comma separated values.

gs650x
  • 383
  • 2
  • 22
  • Thank you Teemu, yes this is a CSV, I have updated my question. – gs650x Apr 28 '20 at 06:00
  • 1
    You can use this cool library `csv-parse` if that is okay – RaR Apr 28 '20 at 06:02
  • Thank you RaR for your response, by any chance would you please suggest a manual approach other than JS library. – gs650x Apr 28 '20 at 06:05
  • Your CSV data is a string? How you assigned that into a variable? – Sifat Haque Apr 28 '20 at 06:07
  • 1
    Parsing CSV data is a non-trivial exercise so using an existing, well tested library would certainly be my preference over writing something bespoke. – Phil Apr 28 '20 at 06:07
  • 1
    use the technique provided in this thread - https://stackoverflow.com/questions/27979002/convert-csv-data-into-json-format-using-javascript – Mukund Apr 28 '20 at 06:10
  • You should leave the quotes. You need them in case there are commas in the data. [I used them to parse the line](https://stackoverflow.com/a/61473879/295783) – mplungjan Apr 28 '20 at 07:21

4 Answers4

2

I would suggest using a dedicated CSV parser, like PapaParse, this is exactly what they are designed to do.

Parsing CSV data can get really tricky once you get into quoting etc.

let csv  = `category,name,includeInAuto,srNumber,testType
"Test1","Name1","true",1,"type1"
"Test2","Name2","true",1,"type2"
"Test3","Name3","true",1,"type3"
"Test4","Name4","true",1,"type4"
"Test5","Name5","true",1,"type5"
"Test6","Name6","true",1,"type6"
"Test7","Name7","true",1,"type7"`;

let result = Papa.parse(csv, { header: true, dynamicTyping: true });
console.log("Result:", result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.1.0/papaparse.min.js"></script> 
Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
  • 1
    Thank you Terry for your answer but I have to use this js code in Salesforce but I cannot use 3rd party libraries in script tag instead I have to download the zip file of thrid party JS and give the reference of that zip file in the code, but if I download the zip file from papaparse's website then size of zip file is around 13 MB but I cannot use zip file of more than 5 mb, would you please suggest on this? – gs650x Apr 28 '20 at 06:55
  • The PapaParse library should be a lot smaller than that, I think it's more like 19kb - https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.1.0/papaparse.min.js. You should be able to download this to include! – Terry Lennox Apr 28 '20 at 06:58
2

Here is my try on your SPECIFIC example WITH qoutes

const parseCsv = csv => {
  let lines = csv.split("\n");
  const header = lines.shift().split(",")
  return  lines.map(line => {
    const bits = JSON.parse("[" + line + "]")
    let obj = {};
    header.forEach((h, i) => obj[h] = bits[i]); // or use reduce here
    // optional:
    obj["includeInAuto"] = obj["includeInAuto"] === "true";
    return obj;
  });
};

const csv = `category,name,includeInAuto,srNumber,testType
"Test1","Name1","true",1,"type1"
"Test2","Name2","true",1,"type2"
"Test3","Name3","true",1,"type3"
"Test4","Name4","true",1,"type4"
"Test5","Name5","true",1,"type5"
"Test6","Name6","true",1,"type6"
"Test7","Name7","true",1,"type7"`

console.log(parseCsv(csv));

Without quotes:

const parseCsv = csv => {
  let lines = csv.split(/\r?\n/);
  const header = lines.shift().split(",")
  return  lines.map(line => {
    const bits = line.split(",")
    let obj = {};
    header.forEach((h, i) => obj[h] = bits[i]); // or use reduce here
    // optional:
    obj["includeInAuto"] = obj["includeInAuto"] === "true";
    return obj;
  });
};

const csv = `category,name,includeInAuto,srNumber,testType
Test1,Name1,true,1,type1
Test2,Name2,true,1,type2
Test3,Name3,true,1,type3
Test4,Name4,true,1,type4
Test5,Name5,true,1,type5
Test6,Name6,true,1,type6
Test7,Name7,true,1,type7`


console.log(parseCsv(csv));

With escaped quotes

const parseCsv = csv => {
  let lines = csv.slice(1,csv.length-1).split(/\r?\n/);
  console.log(lines)
  const header = lines.shift().split(",")
  return  lines.map(line => {
    const bits = line.trim().split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);
    let obj = {};
    header.forEach((h, i) => obj[h] = bits[i].replace(/\"/g,"")); // or use reduce here
    // optional:
    obj["includeInAuto"] = obj["includeInAuto"] === "true";
    return obj;
  });
};

const csv = `"category,name,includeInAuto,srNumber,testType 
    Test1,Name1,true,1,type1 
    Test2,Name2,true,2,type2
    Test3,Name3,true,3,type3
    Test4,Name4,true,4,type4
    Test5,Name5,true,5,type5
    Test6,Name6,true,6,type6
    Test7,\"Name,7\",true,6,type6"`


console.log(parseCsv(csv));
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/212706/discussion-on-answer-by-mplungjan-how-to-convert-csv-to-an-array-of-js-objetcs). – Samuel Liew Apr 28 '20 at 14:34
1

There are several ways to do this. Here's a snippet that uses a reducer for the csv-lines. If the csv gets more complex (e.g. nested " etc), you may want to write a parser (it's fun!) or use some external library to parse csv-files.

const csv = `category,name,includeInAuto,srNumber,testType
"Test1","Name1","true",1,"type1"
"Test2","Name2","true",1,"type2"
"Test3","Name3","true",1,"type3"
"Test4","Name4","true",1,"type4"
"Test5","Name5","true",1,"type5"
"Test6","Name6","true",1,"type6"
"Test7","Name7","true",1,"type7"`.split("\n");

// get headers  
const headers = csv[0].split(",");

// helper to create a row from values 
// (using the just created headers)
const createRow = values => headers.reduce( (acc, header, i) => 
  ({...acc, [header]: values[i]}), {});

// reduce csv-lines to Array of Objects
const csv2Obj = csv
  .slice(1) // no need for headers ofcourse
  .reduce( (acc, row) => ([...acc, createRow(row.replace(/"/g, "").split(","))]), []);

console.log(csv2Obj);
.as-console-wrapper { top: 0; max-height: 100% !important; }
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • I gotta ask, why a regex for `\n`? – Phil Apr 28 '20 at 06:13
  • I suppose it's ... habit ;) – KooiInc Apr 28 '20 at 06:14
  • thank you for your response, the above code works fine if I have the values surrounded in quotes, otherwise I get an error "exception SyntaxError: Unexpected token T in JSON at position 1", I have updated my question as well. – gs650x Apr 28 '20 at 07:30
  • @gs650x `csv2Obj` should be generated *with or without quotes* around the csv values. The error you get is not related to the snippet code, but to something you are doing with JSON (JSON is not a part of the given snippet). – KooiInc Apr 28 '20 at 07:46
0

Here is my solution to solve this.

let csv = `category,name,includeInAuto,srNumber,testType
"Test1","Name1","true",1,"type1"
"Test2","Name2","true",1,"type2"
"Test3","Name3","true",1,"type3"
"Test4","Name4","true",1,"type4"
"Test5","Name5","true",1,"type5"
"Test6","Name6","true",1,"type6"
"Test7","Name7","true",1,"type7"`;


let data = csv.split("\n");
let title = data.shift().split(",");

let array = data.reduce((arr, row) => {
  row = row.replace(/"/g, "");
  let innerObj = row.split(",").reduce((obj, item, index) => {
    obj[title[index]] = item;
    return obj;
  }, {});
  arr.push(innerObj);
  return arr;
}, []);

console.log(array);
Sifat Haque
  • 5,357
  • 1
  • 16
  • 23
  • @mplungjan thanks for the suggestion. I've fixed that. – Sifat Haque Apr 28 '20 at 06:51
  • Your digit is still a string and your code will fail if there are commas in the content: `"Test1","Surname1, FirstName1","true",1,"type1"` - this is not a good idea: `row = row.replace(/"/g, "");` see my version for a better way – mplungjan Apr 28 '20 at 06:54