Convert CSV data into JSON format using Javascript - javascript

I have data in CSV format data and want to convert into JSON format using Javascript.
Following are csv format:
[Test.csv]
id;name;author
integer;string;authors:n
1;To Kill an Angry Bird;1
[authors.csv]
id;name
integer;string
1;Harper Lee
2;JRR Tolkien
3;William Shakespeare
I want to get all the books with their authors. So please how can I implement it using Javascript.

The below should work for you.
All credit to http://techslides.com/convert-csv-to-json-in-javascript
//var csv is the CSV file with headers
function csvJSON(csv){
var lines=csv.split("\n");
var result = [];
// NOTE: If your columns contain commas in their values, you'll need
// to deal with those before doing the next step
// (you might convert them to &&& or something, then covert them back later)
// jsfiddle showing the issue https://jsfiddle.net/
var headers=lines[0].split(",");
for(var i=1;i<lines.length;i++){
var obj = {};
var currentline=lines[i].split(",");
for(var j=0;j<headers.length;j++){
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
//return result; //JavaScript object
return JSON.stringify(result); //JSON
}

I would check out out PapaParse. They have a file called papaparse.min.js that you can drop into your project if need be. PapaParse has no dependencies.
I have used it myself and can verify it works, is convenient, and is well-documented.

Base on #DelightedD0D, I would add if (!lines[i]) continue so it can ignore any empty line and trailing lines.
function csvJSON(csv) {
const lines = csv.split('\n')
const result = []
const headers = lines[0].split(',')
for (let i = 1; i < lines.length; i++) {
if (!lines[i])
continue
const obj = {}
const currentline = lines[i].split(',')
for (let j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j]
}
result.push(obj)
}
return result
}

I think, I have a better solution, this also have one issue i.e. the values should not contain comma(,). Otherwise it is a best solution.
// convert csv to json
csvJSON(csvText) {
let lines = [];
const linesArray = csvText.split('\n');
// for trimming and deleting extra space
linesArray.forEach((e: any) => {
const row = e.replace(/[\s]+[,]+|[,]+[\s]+/g, ',').trim();
lines.push(row);
});
// for removing empty record
lines.splice(lines.length - 1, 1);
const result = [];
const headers = lines[0].split(",");
for (let i = 1; i < lines.length; i++) {
const obj = {};
const currentline = lines[i].split(",");
for (let j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
//return result; //JavaScript object
// return JSON.stringify(result); //JSON
return result;
}
// For Reading CSV File
readCSV(event) {
const reader = new FileReader();
reader.readAsText(event.files[0]);
reader.onload = () => {
const text = reader.result;
const csvToJson = this.csvJSON(text);
console.log(csvToJson);
};
}
Thank you

Here is my try on your SPECIFIC example. I know it is an old question but I have used current methods
const titlesCsv = `id;name;author
integer;string;authors:n
1;To Kill an Mockingbird;1
2;Lord of the Rings;2
3;Hamlet;3`
const authorsCsv = `id;name
integer;string
1;Harper Lee
2;JRR Tolkien
3;William Shakespeare`
const parseCsv = csv => {
let lines = csv.split("\n");
const header = lines.shift().split(";")
lines.shift(); // get rid of definitions
return lines.map(line => {
const bits = line.split(";")
let obj = {};
header.forEach((h, i) => obj[h] = bits[i]); // or use reduce here
return obj;
})
};
const titles = parseCsv(titlesCsv)
const authors = parseCsv(authorsCsv)
const books = titles.map(title => {
return {
id: title.id,
name: title.name,
author: authors.find(author => author.id === title.author).name
}
})
console.log(books)

This solution fixed the comma issue.
function csvJSON(text, quoteChar = '"', delimiter = ',') {
var rows=text.split("\n");
var headers=rows[0].split(",");
const regex = new RegExp(`\\s*(${quoteChar})?(.*?)\\1\\s*(?:${delimiter}|$)`, 'gs');
const match = line => [...line.matchAll(regex)]
.map(m => m[2])
.slice(0, -1);
var lines = text.split('\n');
const heads = headers ?? match(lines.shift());
lines = lines.slice(1);
return lines.map(line => {
return match(line).reduce((acc, cur, i) => {
// replace blank matches with `null`
const val = cur.length <= 0 ? null : Number(cur) || cur;
const key = heads[i] ?? `{i}`;
return { ...acc, [key]: val };
}, {});
});
}
var csvtojson = csvJSON(SOME_CSV_DATA);
console.log(csvtojson)

I have a similar answer like #DelightedD0D but my code can be used in conjunction with Excel directly (copy and paste from Excel into a textarea).
function csvUpload(csvText){
//Split all the text into seperate lines on new lines and carriage return feeds
var allTextLines = csvText.split(/\r\n|\n/);
//Split per line on tabs and commas
var headers = allTextLines[0].split(/\t|,/);
var lines = [];
var locations = [];
for (var i=1; i<allTextLines.length; i++) {
var data = allTextLines[i].split(/\t|,/);
if (data.length == headers.length) {
var location = {"device_id":data[0], "address":data[1], "city":data[2]};
locations.push(location);
}
}
return locations;
}
This way you can use a CSV that is copied into Excel. Excel will remove the seperators like , and others and will insert newlines etc.
With the my code you can pass everything into a textfield directly from Excel and then use that to create a json.
I have the naming of the fields static here, but you could use #DelightedD0D's code to set the headers dynamically:
for(var j=0;j<headers.length;j++){
obj[headers[j]] = currentline[j];
}

Related

JavaScript: CSV records in to an array of objects with string and number

Hello i am new in JavaScript and i would like to know how to get an array of objects with strings and an array with numbers. Is it possible to use parseInt to convert the array with the numbers in my CSV ?
this is my CSV-Data
word; arrayInt
hello; [1,20,30]
bye; [4,50,60]
this is my output
[{"word":"hello"," arrayInt":" [1,20,30]"},{"word":"bye"," arrayInt":" [4,50,60]"}]
the code i use with the CSV-Data at the top
<head> </head>
<body>
<form id="myForm">
<input type="file" id="csvFile" accept=".csv" />
<br />
<input type="submit" value="Submit" />
</form>
<script>
const myForm = document.getElementById("myForm");
const csvFile = document.getElementById("csvFile");
function csvToArray(str, delimiter = ";") {
// slice from start of text to the first \n index
// use split to create an array from string by delimiter
const headers = str.slice(0, str.indexOf("\n")).split(delimiter);
// slice from \n index + 1 to the end of the text
// use split to create an array of each csv value row
const rows = str.slice(str.indexOf("\n") + 1).split("\n");
// Map the rows
// split values from each row into an array
// use headers.reduce to create an object
// object properties derived from headers:values
// the object passed as an element of the array
const arr = rows.map(function (row) {
const values = row.split(delimiter);
const el = headers.reduce(function (object, header, index) {
object[header] = values[index];
return object;
}, {});
return el;
});
// return the array
return arr;
}
myForm.addEventListener("submit", function (e) {
e.preventDefault();
const input = csvFile.files[0];
const reader = new FileReader();
reader.onload = function (e) {
const text = e.target.result;
const data = csvToArray(text);
var myJSON = JSON.stringify(data);
console.log(myJSON); // Output of the CSV-Data
var tmp = myJSON;
for (var i = 0; i < tmp.length; i++) {
let member = tmp[i];
let arr = JSON.parse(member.arrayInt);
tmp[i].arrayInt = arr;
};
console.log(tmp);
};
reader.readAsText(input);
});
</script>
</body>
the output i want to achieve
[{word:"hello", arrayInt: [1,20,30]},{word:"bye", arrayInt: [4,50,60]}]
What about this?
var tmp = [{"word":"hello","arrayInt":"[1,20,30]"},{"word":"bye","arrayInt":"[4,50,60]"}];
for(var i = 0; i < tmp.length; i++) {
let member = tmp[i];
let arr = JSON.parse(member.arrayInt);
tmp[i].arrayInt = arr;
}
console.log(tmp);
/* outputs
[
{ word: 'hello', arrayInt: [ 1, 20, 30 ] },
{ word: 'bye', arrayInt: [ 4, 50, 60 ] }
]
*/
EDIT
You original code has some issues. You would need to change the following in your code, in order for my suggestion to work.
In the following:
const arr = rows.map(function (row) {
const values = row.split(delimiter);
const el = headers.reduce(function (object, header, index) {
//object[header] = values[index]; // <=== change this
object[header.trim()] = values[index].trim(); // <=== into this
return object;
}, {});
return el;
});
one change is needed in order to clear the spaces you have around the keys and values in your original CSV.
Next, this:
var myJSON = JSON.stringify(data);
console.log(myJSON); // Output of the CSV-Data
var tmp = myJSON;
needs to become like this
//var myJSON = JSON.stringify(data);
//console.log(myJSON); // Output of the CSV-Data
var tmp = data;
We are doing this because there's no need to stringify the data. We need to have it as an array of objects, and we'll work with that.
The rest of your the code stays as it is.

How do I convert a string to a dictionary in javascript?

I have a string
var str = "1:6,5,2,2:3";
I want to convert this str into a js dictionary such that:
var dict = {1:"6,5,2",
2:"3"};
so that I can fetch the values by their respective key index. How do I convert it?
I had tried this code to store the splitted values into an array:
var pages = "1:6,5,2,2:3";
var numbers = [];
if (pages.includes(',')) {
page_nos = pages.split(',');
for (var i = 0; i < page_nos.length; i++) {
if (page_nos[i].includes(':')) {
var n = page_nos[i].split(':');
numbers.push(n[1]);
} else {
numbers.push(page_nos[i]);
}
}
} else {
page_nos = pages.split(':');
numbers.push(page_nos[1])
};
console.log('numbers: ', numbers);
But it's incorrect, as without dictionary it's impossible to know what value belongs to which index
If you cannot make your input string a proper JSON or another easily parsable format in the first place, this answers your question:
const str = "1:6,5,2,2:3";
const obj = str.split(/,(?=\d+:)/).reduce((accu, part) => {
const [k, v] = part.split(':', 2);
accu[k] = v;
return accu;
}, {});
console.log(obj);
Cut the string at all commas that are followed by digits and a colon. Each part has a key in front of a colon and a value after it, which should be stuffed in an object in this format.
No mutations solution.
const str = "1:6,5,2,2:3";
const dict = str
.split(/(\d+:.*)(?=\d+:)/g)
.reduce((t, c) => {
const [key, value] = c.replace(/,$/, "").split(/:/);
return { ...t, [key]: value }
});
console.log(dict);
if you consider not using regular expression, you might try this as well.
to take out a dict (Object) from that string, this will do.
var pages = "1:6,5,2,2:3";
function stringToObject(str) {
var page_object = {};
var last_object;
str.split(",").forEach((item) => {
if (item.includes(":")) {
page_object[item.split(":")[0]] = item.split(":")[1];
last_object = item.split(":")[0];
} else {
page_object[last_object] += `,${item}`;
}
});
return page_object;
}
console.log(stringToObject(pages))
Presented below may be one possible solution to achieve the desired objective.
NOTE:
In lieu of var the code uses either let or const as applicable.
Code Snippet
const pages = "1:6,5,2,2:3";
const resObj = {};
let page_nos, k;
if (pages.includes(',')) {
page_nos = pages.split(',');
for (let i = 0; i < page_nos.length; i++) {
if (page_nos[i].includes(':')) {
let n = page_nos[i].split(':');
k = n[0];
resObj[k] = n[1].toString();
} else {
resObj[k] += ", " + page_nos[i].toString();
}
}
} else {
page_nos = pages.split(':');
resObj[page_nos[0]] = [page_nos[1]]
numbers.push(page_nos[1])
};
console.log('result object: ', resObj);
This code essentially fixes the code given in the question. It is self-explanatory and any specific information required may be added based on questions in comments.
You could take nested splitring for entries and get an object from it.
const
str = "1:6,5,2,2:3",
result = Object.fromEntries(str
.split(/,(?=[^,]*:)/)
.map(s => s.split(':'))
);
console.log(result);

How to transform a plain text file to JSON [duplicate]

I have data in CSV format data and want to convert into JSON format using Javascript.
Following are csv format:
[Test.csv]
id;name;author
integer;string;authors:n
1;To Kill an Angry Bird;1
[authors.csv]
id;name
integer;string
1;Harper Lee
2;JRR Tolkien
3;William Shakespeare
I want to get all the books with their authors. So please how can I implement it using Javascript.
The below should work for you.
All credit to http://techslides.com/convert-csv-to-json-in-javascript
//var csv is the CSV file with headers
function csvJSON(csv){
var lines=csv.split("\n");
var result = [];
// NOTE: If your columns contain commas in their values, you'll need
// to deal with those before doing the next step
// (you might convert them to &&& or something, then covert them back later)
// jsfiddle showing the issue https://jsfiddle.net/
var headers=lines[0].split(",");
for(var i=1;i<lines.length;i++){
var obj = {};
var currentline=lines[i].split(",");
for(var j=0;j<headers.length;j++){
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
//return result; //JavaScript object
return JSON.stringify(result); //JSON
}
I would check out out PapaParse. They have a file called papaparse.min.js that you can drop into your project if need be. PapaParse has no dependencies.
I have used it myself and can verify it works, is convenient, and is well-documented.
Base on #DelightedD0D, I would add if (!lines[i]) continue so it can ignore any empty line and trailing lines.
function csvJSON(csv) {
const lines = csv.split('\n')
const result = []
const headers = lines[0].split(',')
for (let i = 1; i < lines.length; i++) {
if (!lines[i])
continue
const obj = {}
const currentline = lines[i].split(',')
for (let j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j]
}
result.push(obj)
}
return result
}
I think, I have a better solution, this also have one issue i.e. the values should not contain comma(,). Otherwise it is a best solution.
// convert csv to json
csvJSON(csvText) {
let lines = [];
const linesArray = csvText.split('\n');
// for trimming and deleting extra space
linesArray.forEach((e: any) => {
const row = e.replace(/[\s]+[,]+|[,]+[\s]+/g, ',').trim();
lines.push(row);
});
// for removing empty record
lines.splice(lines.length - 1, 1);
const result = [];
const headers = lines[0].split(",");
for (let i = 1; i < lines.length; i++) {
const obj = {};
const currentline = lines[i].split(",");
for (let j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
//return result; //JavaScript object
// return JSON.stringify(result); //JSON
return result;
}
// For Reading CSV File
readCSV(event) {
const reader = new FileReader();
reader.readAsText(event.files[0]);
reader.onload = () => {
const text = reader.result;
const csvToJson = this.csvJSON(text);
console.log(csvToJson);
};
}
Thank you
Here is my try on your SPECIFIC example. I know it is an old question but I have used current methods
const titlesCsv = `id;name;author
integer;string;authors:n
1;To Kill an Mockingbird;1
2;Lord of the Rings;2
3;Hamlet;3`
const authorsCsv = `id;name
integer;string
1;Harper Lee
2;JRR Tolkien
3;William Shakespeare`
const parseCsv = csv => {
let lines = csv.split("\n");
const header = lines.shift().split(";")
lines.shift(); // get rid of definitions
return lines.map(line => {
const bits = line.split(";")
let obj = {};
header.forEach((h, i) => obj[h] = bits[i]); // or use reduce here
return obj;
})
};
const titles = parseCsv(titlesCsv)
const authors = parseCsv(authorsCsv)
const books = titles.map(title => {
return {
id: title.id,
name: title.name,
author: authors.find(author => author.id === title.author).name
}
})
console.log(books)
This solution fixed the comma issue.
function csvJSON(text, quoteChar = '"', delimiter = ',') {
var rows=text.split("\n");
var headers=rows[0].split(",");
const regex = new RegExp(`\\s*(${quoteChar})?(.*?)\\1\\s*(?:${delimiter}|$)`, 'gs');
const match = line => [...line.matchAll(regex)]
.map(m => m[2])
.slice(0, -1);
var lines = text.split('\n');
const heads = headers ?? match(lines.shift());
lines = lines.slice(1);
return lines.map(line => {
return match(line).reduce((acc, cur, i) => {
// replace blank matches with `null`
const val = cur.length <= 0 ? null : Number(cur) || cur;
const key = heads[i] ?? `{i}`;
return { ...acc, [key]: val };
}, {});
});
}
var csvtojson = csvJSON(SOME_CSV_DATA);
console.log(csvtojson)
I have a similar answer like #DelightedD0D but my code can be used in conjunction with Excel directly (copy and paste from Excel into a textarea).
function csvUpload(csvText){
//Split all the text into seperate lines on new lines and carriage return feeds
var allTextLines = csvText.split(/\r\n|\n/);
//Split per line on tabs and commas
var headers = allTextLines[0].split(/\t|,/);
var lines = [];
var locations = [];
for (var i=1; i<allTextLines.length; i++) {
var data = allTextLines[i].split(/\t|,/);
if (data.length == headers.length) {
var location = {"device_id":data[0], "address":data[1], "city":data[2]};
locations.push(location);
}
}
return locations;
}
This way you can use a CSV that is copied into Excel. Excel will remove the seperators like , and others and will insert newlines etc.
With the my code you can pass everything into a textfield directly from Excel and then use that to create a json.
I have the naming of the fields static here, but you could use #DelightedD0D's code to set the headers dynamically:
for(var j=0;j<headers.length;j++){
obj[headers[j]] = currentline[j];
}

Find common words and push into new array

I have Array below and attached output.
`var
arrayData=['cat','mat','tac','hiller','mamaer','llerih','eramam'];
output : [[cat,tac], [mat], [hiller,llerih], [mamaer,erama]];`
Want to extract common letter in an array and store in new array.
i was trying to implement using array.reducer.
You can do this with Map and a simple loop. The idea is you record each word by creating a "key" from the sum of their character code and length of the word.
Used Array#Reduce to sum the words character codes.
i.e.:
//sum of each letter's character code using reduce
const res1 = "test".split("").reduce((a,c)=>a+c.charCodeAt(), 0);
const res2 = "ttes".split("").reduce((a,c)=>a+c.charCodeAt(), 0);
const l = "test".length;
const key1 = `${l}_${res1}`;
const key2 = `${l}_${res2}`;
console.log(key1, key2, key1 === key2); //4_448 4_448 true
i.e. (without reduce and with for loop):
function sum(word){
let s = 0;
for(let i = 0; i < word.length; i++){
s += word[i].charCodeAt();
}
return s
}
//sum of each letter's character code using reduce
const res1 = sum("test");
const res2 = sum("ttes");
const l = "test".length;
const key1 = `${l}_${res1}`;
const key2 = `${l}_${res2}`;
console.log(key1, key2, key1 === key2); //4_448 4_448 true
Recording the length of the word adds an extra level of security incase two different words of lengths different had the same sum
Full Solution:
const data = ['cat','mat','tac','hiller','mamaer','llerih','eramam'];
const m = new Map();
for(let i = 0; i < data.length; i++){
const word = data[i];
const sum = word.split("").reduce((a,c)=>a+c.charCodeAt(), 0);
const key = `${word.length}_${sum}`;
m.set(key, [word].concat(m.get(key)||[]));
}
const res = Array.from(m.values());
console.log(res);
I solved it with a different way, I sorted them, grouped them then displayed them by index.
var arrayData=['cat','mat','tac','hiller','mamaer','llerih','eramam'];
var sortedArrayData = arrayData.map(itm => itm.split('').sort((a,b) => a>b).join(''));
var data = {};
sortedArrayData.forEach((itm, indx) => {
if(!data[itm]) data[itm] = [];
data[itm].push(indx)
});
var result = Object.keys(data).map(key => {
return data[key].map(it => arrayData[it])
})
console.log(result)
try it here

Issues with JSON formatting for data object in Grafana

Data is not coming in with proper JSON formatting, so I'm having to loop through items in the array to fix the formatting, parsing the changed items and I cannot use the new object(s) when everything is finished because it is no longer in an array. The data is coming in as follows:
data [datapoints: [0..1..]
target: "up{cluster="bluehills_c3260_cluster",component="atr",datacenter="bluehills",hostname="ny-153-177"...}"]
Is there an easier way to convert this using a .map function or some other method to make things cleaner and get the desired result?
I've tried several methods including .replace, .map, and .push. I've also tried JSON.stringify, but nothing else seems to work except what I currently have.
onDataReceived(data) {
var i;
for (i = 0; i < data.length; i++) { // Loop through data array
var txt = data[i].target; // Create the variable to store the data target
var j;
for (j = 0; j <= txt.length; j++) { // Loop through the data target
var newObj = txt.slice(2,j); // Remove "up"
var filteredObj = newObj // Change over to JSON format...
.replace(/=/g,' : ')
.replace(/,/g,', ')
.replace(/{/g,'{ ')
.replace(/cluster/g,'"cluster"')
.replace(/component/g,'"component"')
.replace(/datacenter/g,'"datacenter"')
}
var dataObj = filteredObj.replace(/_"cluster"/gi,'_cluster');
var finalObj = JSON.parse(dataObj);
console.log("finalObj", dataObj);
}
}
What I want is a single array with the proper JSON format for the data (target) coming in.
How about this?
const myReg = /([\w\s]+)=\"([^"]*)\"/g
const str = `data [datapoints: [0..1..] target: "up{cluster="bluehills_c3260_cluster",component="atr",datacenter="bluehills",hostname="ny-153-177"...}"]`;
let matches = null;
const resultsJson = {};
while(matches = myReg.exec(str)){
resultsJson[matches[1]] = matches[2];
}
{ cluster: 'bluehills_c3260_cluster',
component: 'atr',
datacenter: 'bluehills',
hostname: 'ny-153-177' }
Not sure if this is how you want to have the data stored but that part would be pretty easy to customize.
onDataReceived(data){
this.createCosmo(data);
}
createCosmo(data) {
var arr = $.map(data,function(value){
return value.target;
});
var arr2 = $.map(arr,function(value){
var newObj = value.slice(2); // Remove "up"
var filteredObj = newObj // Change over to JSON format
.replace(/=/g,' : ')
.replace(/,/g,', ')
.replace(/{/g,'{ ')
.replace(/cluster/g,'"cluster"')
.replace(/component/g,'"component"')
.replace(/datacenter/g,'"datacenter"')
.replace(/hostname/g,'"hostname"')
.replace(/instance/g,'"instance"')
.replace(/job/g,'"job"')
.replace(/resilience_group/g,'"resilience_group"')
.replace(/_"cluster"/gi,'_cluster')
.replace(/-"cluster"/gi,'-cluster');
var finalObj = JSON.parse(filteredObj); // Parse the Object into JSON
return finalObj;
});
}

Categories