Unexpected quoted property in dynamically created object - javascript

I try to load contents of the CSV file into the array of JavaScript objects. Unfortunately, structure of data is not flat, so to mitigate that, I use dot-separated property names as column headers in CSV file. Example CSV file looks as follows:
codes.code1,codes.code2,codes.code3,codes.code4,info.description.text,info.description.language
49074202,64,1443,1416,Test description: 49074202 64 1443 1416,EN
81905948,10,9721,5411,Test description: 81905948 10 9721 5411,EN
87262350,86,7050,4775,Test description: 87262350 86 7050 4775,EN
The object structure I would like to end up with looks like
{
codes: {
code1: "49074202",
code2: "64",
code3: "1443",
code4: "1416"
},
info: {
description: {
text: "Test description: 49074202 64 1443 1416",
language: "EN"
}
}
}
Following code can be used to recreate the problem (using CSV file with contents posted above).
<html>
<body>
<form>
<input type="file" id="files" onchange="loadFromFile()" />
<script>
function loadFromFile() {
var selectedFile = document.getElementById('files').files[0];
var reader = new FileReader();
reader.onload = loadRowsFromFile;
reader.readAsText(selectedFile);
};
function loadRowsFromFile(e) {
var rows = e.target.result.split("\n");
if (!rows || rows.length === 0) {
return;
}
var headers = rows[0].split(",");
var loadedData = [];
for (var i = 1; i < rows.length; i++) {
var columns = rows[i].split(",");
var rowData = {};
for (var j = 0; j < headers.length; j++) {
placeElementInHierarchy(rowData, headers[j], columns[j]);
}
loadedData.push(rowData);
}
console.log(loadedData);
}
function placeElementInHierarchy(rowData, propertyPath, value) {
var path = propertyPath.split(".");
var obj = rowData;
for (var i = 0; i < path.length; i++) {
if (i === path.length - 1) {
obj[path[i]] = value;
} else {
if (!obj[path[i]]) {
obj[path[i]] = {};
}
obj = obj[path[i]];
}
}
}
</script>
</form>
</body>
</html>
File parsing works properly, data is loaded correctly into the structure. However, for some reason the last processed property name is quoted. Structure of all rows I obtain looks like that:
{
codes: {
code1: "49074202",
code2: "64",
code3: "1443",
code4: "1416"
},
info: {
description:{
text: "Test description: 49074202 64 1443 1416",
"language": "EN"
}
}
}
The only lead I have right now is that if I remove the 'language' column from CSV file, then 'text' is being quoted instead - so the last processed property is for some reason quoted. Thanks in advance!
EDIT: Fixed mistakes in the expected structure.
EDIT2: What is interesting, following code (skipping file upload part), doesn't show such symptoms
<html>
<body>
<form>
<script>
(function() {
var csvData = 'codes.code1,codes.code2,codes.code3,codes.code4,info.description.text,info.description.language\n' +
'49074202,64,1443,1416,Test description: 49074202 64 1443 1416,EN\n' +
'81905948,10,9721,5411,Test description: 81905948 10 9721 5411,EN\n' +
'87262350,86,7050,4775,Test description: 87262350 86 7050 4775,EN';
loadRowsFromFile(csvData);
})();
function loadRowsFromFile(csvData) {
var rows = csvData.split("\n");
if (!rows || rows.length === 0) {
return;
}
var headers = rows[0].split(",");
var loadedData = [];
for (var i = 1; i < rows.length; i++) {
var columns = rows[i].split(",");
var rowData = {};
for (var j = 0; j < headers.length; j++) {
placeElementInHierarchy(rowData, headers[j], columns[j]);
}
loadedData.push(rowData);
}
console.log(loadedData);
}
function placeElementInHierarchy(rowData, propertyPath, value) {
var path = propertyPath.split(".");
var obj = rowData;
for (var i = 0; i < path.length; i++) {
if (i === path.length - 1) {
obj[path[i]] = value;
} else {
if (!obj[path[i]]) {
obj[path[i]] = {};
}
obj = obj[path[i]];
}
}
}
</script>
</form>
</body>
</html>

Okay. As you can see in screen above, your problem is generated by Carriage Return character \r on end of every line in the file. You can remove all your empty characters from string using
trim()
or you can use regexp to cut out just this type of characters.
Example is done here:
How to remove all line breaks from a string?

Related

Convert excel to json but with only one header

I am trying to write an html with JS program that will convert an excel file into json which is does bit it does not format it the way I need to. So basically it spits out when finished
[
{
"imei": "357271093291264"
},
{
"imei": "353094106032150"
},
{
"imei": "353112106434588"
}
]
but what I need is.
[
{
"imei": "357271093291264", "353094106032150", "353112106434588"
}
]
So it is taking imei from cell A1 and using it over and over. I just need it
to keep adding on as I go down the rows.
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/read-excel-file#4.x/bundle/read-excel-file.min.js"></script>
</head>
<body>
<div style="margin: auto;width: 50;margin-top: 80px;padding: 30px;background-color: #dedede;">
<h2>Excel to JSON Converter</h2>
<input type="file" id="input" />
<br> <br>
<textarea name="json-data" id="json-data" rows="25" style="width: 100%;"></textarea>
<br><br>
<button id="dl-json">Download JSON File</button>
</div>
<script>
var input = document.getElementById('input');
input.addEventListener('change', function(){
readXlsxFile(input.files[0]).then(function(data){
var i = 0;
var headers = [];
var json_object = [];
data.map((row, index)=> {
if (i == 0){
headers = row;
}
if (i > 0){
var temp = {};
for (var x = 0; x < row.length; x++){
temp[headers[x]] = row[x];
}
json_object.push(temp);
}
i++;
});
document.getElementById('json-data').value = JSON.stringify(json_object, null, 2)
});
document.getElementById('dl-json').onclick = function() {
var json_str = document.getElementById('json-data').value;
downloadObjectAsJson(json_str, '');
}
function downloadObjectAsJson(str, filename){
var data_str = "data:text/json;charset=utf-8," + encodeURIComponent(str);
var anchor = document.createElement('a');
anchor.setAttribute("href", data_str);
anchor.setAttribute("download", filename + ".json");
}
});
</script>
</body>
</html>
I have tried playing around with it and pulling out certain parts and setting different variables to certain values.
The shape of your output doesn't seem to make sense. Do you want the first element in your output array to be a key:value pair such as "headerText":"row2Value", and then the rest just strings?
If so, this should work for you:
var input = document.getElementById("input");
input.addEventListener("change", function () {
readXlsxFile(input.files[0]).then(function (data) {
let exportData = [];
for (i = 1; i < data.length; i++) {
i === 1
? exportData.push({ imei: data[i].toString() })
: exportData.push(data[i].toString());
}
document.getElementById("json-data").value = JSON.stringify(exportData);
});
document.getElementById("dl-json").onclick = function () {
var json_str = document.getElementById("json-data").value;
downloadObjectAsJson(json_str, "");
};
function downloadObjectAsJson(str, filename) {
var data_str =
"data:text/json;charset=utf-8," + encodeURIComponent(str);
var anchor = document.createElement("a");
anchor.setAttribute("href", data_str);
anchor.setAttribute("download", filename + ".json");
}
});
If you only need the key, then an array of values, this will work better for you:
readXlsxFile(input.files[0]).then(function (data) {
let exportData = [];
for (i = 1; i < data.length; i++) {
exportData.push(data[i].toString());
}
document.getElementById("json-data").value = JSON.stringify({
imei: exportData,
});
});

How do I read a cvs file in javascript and store them in map?

say if I have csv file with :
Heading 1 , Heading 2 , Heading 3
Value 1 , Value2 , Value 3
All I want is to create a map that stores Heading 1 as a key and Heading 2 as value;
like map.set(value1 , value2)
How do I do this while I read the file in javascript ?
function processData(allText) {
var allTextLines = allText.split("\r");
for (var i=1; i<allTextLines.length; i++) {
var data = allTextLines[i].split(',');
console.log(data[0]);
map1.set(data[0] , data[1]);
}
}
so far I tried to do this . But it doesn't work. It doesn't read the file at all. Any help is appreciated.
Thanks
If you have a series of items separated by commas (,), the you can iterate the String and explode or split the items. This can be done with Vanilla JavaScript. The magic part is the for() loop; iterating it by 2 instead of by 1, which is most commonly seen.
$(function() {
var myString = "Header 1,Value 1,Header 2,Value 2,Header 3,Value 3";
var parts = myString.split(",");
var myData = {};
for (var i = 0; i < parts.length; i += 2) {
myData[parts[i]] = parts[i + 1];
}
console.log(myData);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If your file has multiple lines, and the first line is Headers, for example:
Header 1,Header 2,Header 3
Value 1,Value 2,Value 3
Value 4,Value 5,Value 6
You'll have to treat it differently. When it's brought into JS, it will be one big String, and you will have to first split it by End Of Line (EOL). This will create an Array of Strings that must be iterated. You will want to make an Array of Keys and then a Matrix of Values.
Since the file is Local, you will need to first get the File from the User. This is discussed here: How to read data From *.CSV file using javascript? and here: Reading in a local csv file in javascript? You will have to determine the best method for yourself.
One way is to use a File Input. There are drawbacks and caveats due to security and browsers, but it might work.
$(function() {
var fileInput = $("#getFile");
function toObj(keys, vals) {
var obj = {};
for (var i = 0; i < keys.length; i++) {
obj[keys[i]] = vals[i];
}
return obj;
}
function stringToObject(str, header) {
if (header == undefined) {
header = false;
}
var lines = str.split("\n");
var k = [],
m = [];
if (header) {
k = lines.splice(0, 1);
k = k[0].split(",");
}
$.each(lines, function(i, el) {
if (el.length) {
m.push(el.split(","));
}
});
if (k.length) {
var r = [];
$.each(m, function(i, el) {
r.push(toObj(k, el));
});
return r;
} else {
return m;
}
}
function readFile() {
var reader = new FileReader();
reader.onload = function() {
var newData = stringToObject(reader.result, $("#header").prop("checked"));
console.log(newData);
$("#out").html("<pre>" + reader.result + "</pre>");
};
reader.readAsBinaryString(fileInput[0].files[0]);
};
fileInput.change(readFile);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="file input">
<input type="checkbox" id="header" checked="checked"> <label>CSV Header</label><br />
<input type="file" id="getFile" />
</div>
<div id="out"></div>

Pushing values to getColumn.values for Excel add In

So I am trying to import and read a json to an excel sheet, using an add-in I'm developing. So I've gotten to a point where I'm getting ColumnA and ColumnB from my new worksheet. Then I'm trying to push the json fields onto the Range.values arrays of the columns. However once I run the program the worksheet is still blank. Here is the code:
function importJson(json, name){
Excel.run(function (context) {
...
var sheetRange = newWorksheet.getRange("A1:B1");
sheetRange.load('values');
var aColumn = sheetRange.getColumn(0);
var bColumn = sheetRange.getColumn(1);
aColumn.load('values');
bColumn.load('values');
return context.sync().then(function () {
printJson(json, aColumn, bColumn);
});
});
printJson(json, aColumn, bColumn)
{
if (json instanceof Object) {
aColumn.values.push(json.display);
if (json.default != null) {
bColumn.values.push(json.default);
}
}
if (json.fields != null) {
for (var i = 0; i < json.fields.length; i++) {
printSchema(json.fields[i], aColumn, bColumn);
}
}
}
Running the debugger I see the values from the json object being pushed onto the arrays but run I still don't see them on the worksheet
Thanks for any help!
So Thanks to a little nudge from #TimWilliams, it was realized that I wasn't updating the worksheets values within my printJson method. So once I pushed all of the values I wanted in Column A and Column B I added this step in my last return sync().then(function(){})....
Excel.run(function (context) {
...
var sheetRange = newWorksheet.getRange("A1:B1");
sheetRange.load('values');
var aColumn = sheetRange.getColumn(0);
var bColumn = sheetRange.getColumn(1);
aColumn.load('values');
bColumn.load('values');
return context.sync().then(function () {
printJson(json, aColumn, bColumn);
****
for (var i = 1; i < aColumn.values.length + 2; i++)
{
var aColumnSheet = newWorksheet.getRange("A" + i);
aColumnSheet.values = aColumn.values[i];
}
for (var i = 1; i < bColumn.values.length + 2; i++) {
var bColumnSheet = newWorksheet.getRange("B" + i);
bColumnSheet.values = bColumn.values[i];
}
*****
});
});
Gives me two beautiful columns of data in Column A and Column B. Thanks again #TimWilliams!

Getting trouble while trying Dynamic Data in jsPdf AutoTable

Am trying to print the dynamic data into the PDF using jsPdf AutoTable .But am failed to do that. I searched in many site's but no one didn't said about dynamic data into the Row's. So here my question is , Is there any way to get the Dynamic data into the table row's if it so can some one clarify me pls . Note : [ Here am not using HTML to store the Data into the Pdf, i got the data from the js directly ] .
this.print=function(){
{
var mainData =this.printData(); // Here am getting Full Json data Here
var steps = mainData.steps; // From that data am Separating what i need
var criticality = mainData.criticality;
var categories = mainData.categories;
var checkup = mainData.checkup;
// This is For to Take the Steps Data alone
$scope.getSteps = function(steps) {
var data = [];
for (var i = steps.length; i-- > 0;) {
data.push(steps[i].name+"\n"+"\n");
}
return data;
}
// Like wise am getting every single object data's
$scope.getNumbersOfSubSteps = function(steps) {
var data = 0;
for (var i = 0 ; i < steps.length; i++) {
for (var j = 0; j<steps[i].steps.length; j++) {
}
data = j ;
}
return data;
}
// this is for Sub Proceeses
$scope.getSubProcesses = function(steps) {
var data = [];
for (var i = 0 ; i < steps.length; i++) {
for (var j = 0; j<steps[i].steps.length; j++) {
data.push(steps[i].steps[j].name+"\n");
}
}
return data;
}
$scope.getCategories = function(categories) {
var data = [];
for (var i = categories.length; i-- > 0;) {
data.push(categories[i].name+"\n");
}
return data;
}
$scope.getCriticality = function(criticality) {
var data = [];
for (var i = criticality.length; i-- > 0;) {
data.push(criticality[i].name+"\n");
}
return data;
}
// Pdf Print Function Begins
var columns = ["ProcessDescription", "Steps", "#ofSubProcesses", "SubSteps","Category","Criticality","CheckUp"];
var processDescription =mainData.description;
var processes= $scope.getSteps(steps);
var NoOfSubProcess = $scope.getNumbersOfSubSteps(steps);
var subProcesses = $scope.getSubProcesses(steps);
console.log('Subprocsses length',subProcesses);
var categories = $scope.getCategories(categories);
var criticality = $scope.getCriticality(criticality);
// The Problem Begins here , Am struggling to Get the Separate data's here !
var rows = [
[processDescription,processes,NoOfSubProcess,subProcesses,categories,criticality]
];
var pdfsize='a1';
var doc = new jsPDF('p', 'pt',pdfsize);
doc.autoTable(columns, rows, {
theme: 'striped', // 'striped', 'grid' or 'plain'
styles: {
overflow: 'linebreak',
columnWidth: 'wrap'
},
beforePageContent: function(data) {
doc.text("Process Name :"+mainData.name, 40, 30);
},
columnStyles: {
1: {columnWidth: 'auto'}
}
});
doc.save(mainData.name+ pdfsize +".pdf");
}
};
You will need to replace this:
var rows = [
[processDescription,processes,NoOfSubProcess,subProcesses,categories,criticality]
];
with something like this:
var rows = [];
for (var k = 0 ; k < processes.length; k++) {
rows.push([
processDescription,
processes[k],
NoOfSubProcess,
subProcesses[k],
categories[k],
criticality[k]
]);
};
The rows parameter should be an array of arrays. What you are putting in there is basically an array of an array of arrays if I understood correctly.

Push data to different elements, depending on column in google spreadsheet with GAS?

I am working in Google Spreadsheet with GAS, and I am trying to push some data from a spreadsheet to an HTML page, and right now that is working. But I am managing to do is grabbing all the values, and each time it hits a new row, it grabs all the values in that row pushes them into a newly created <div>. But I would like to do is have some column functionality also so that the different columns gets pushed to a different element like an <input> or a <select> element.
I tried out some things where I declared some variables for the desired columns, and trying to pushing them to the HTML one by one, but it didn't work out.
Here is my data:
This is what it looks like in the HTML:
This is what I am going for:
Would it be better to publish this into tables? Because I simply thought of creating divs with classes and set their width and line breaks?
Below is the code I described in the beginning:
Code.gs
var ss = SpreadsheetApp.openById("1c7IwmyBrbNq5xwzo-7EyFewCx31WpfP4EzLpkHawffI");
function doGet(request) {
return HtmlService.createTemplateFromFile('stuff')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getStitchOrders(){
var ordersName = [];
var sheet = ss.getSheetByName("Cat1");
var subRange = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn());
var orders = subRange.getValues();
for (var i = 0; i < orders.length; i++) {
ordersName.push( orders[i] )
}
return ordersName;
}
stuff.html
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="orders">
//Data is listed here.
</div>
<script>
$(function() {
google.script.run.withSuccessHandler(buildOrderList).getStitchOrders();
});
function buildOrderList(ordersName) {
var rows = $('#orders');
for (var i = 0; i < ordersName.length; i++) {
rows.append('<div name="' + ordersName[i] + '">' + ordersName[i] + '</div>');
}
}
</script>
Any suggestions?
Edit
Code2.gs
var ss = SpreadsheetApp.openById("1c7IwmyBrbNq5xwzo-7EyFewCx31WpfP4EzLpkHawffI");
function doGet(request) {
return HtmlService.createTemplateFromFile('stuff2')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getStitchOrders(){
var ordersName = [];
var sheet = ss.getSheetByName("Sheet");
var subRange = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn());
var orders = subRange.getValues();
for (var i = 0; i < orders.length; i++) {
ordersName.push( {
name: orders[i][0],
status: orders[i][1],
comment: orders[i][2]
} );
}
return JSON.stringify(ordersName);
}
stuff2.html
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="orders">
</div>
<script>
$(function() {
google.script.run.withSuccessHandler(buildOrderList).getStitchOrders();
});
function buildOrderList(ordersName) {
var arr = JSON.Parse(ordersName);
var rows = $('#orders');
for (var i = 0; i < arr.length; i++) {
rows.append('<div name="' + arr[i].name + '">' + arr[i].name + '</div>');
}
}
</script>
consider returning the data with this type of pattern:
for (var i = 0; i < orders.length; i++) {
ordersName.push( {
name: orders[i][0],
status: orders[i][1],
comment: orders[i][2]
} );
}
return JSON.stringify(ordersName);
then back in the client-side JS we can turn it back into an Array to loop through:
function buildOrderList(ordersName) {
var arr = JSON.parse(ordersName);
var rows = $('#orders');
for (var i = 0; i < arr.length; i++) {
// values can now be referenced via...
// arr[i].name
// arr[i].status;
// arr[i].comment;
rows.append(...);
}
}
how to then style the divs and the elements inside them to align up like a table is more a CSS question.

Categories

Resources