I'm trying to read an excel file and create a multidimensional array in javascript with it.
The excel file will look like:
AA11 AA22 AN65
AB11 AB22 AN64
...
I need it to create an array that looks like:
[
[AA11, AA22, AN65],
[AB11, AB22, AN64]
]
So far, I've been able to bring up a file selection window, and I believe it's reading the file, I just think it might not be putting the data into the array correctly. This is what I have so far:
<script type="text/javascript">
$(function () {
$("#input").on("change", function () {
var excelFile,
var array = [[],[]];
fileReader = new FileReader();
$("#result").hide();
fileReader.onload = function (e) {
var buffer = new Uint8Array(fileReader.result);
$.ig.excel.Workbook.load(buffer, function (workbook) {
var column, row, newRow, cellValue, columnIndex, i,
worksheet = workbook.worksheets(0),
columnsNumber = 0,
gridColumns = [],
data = [],
worksheetRowsCount;
while (worksheet.rows(0).getCellValue(columnsNumber)) {
columnsNumber++;
}
for (columnIndex = 0; columnIndex < columnsNumber; columnIndex++) {
column = worksheet.rows(0).getCellText(columnIndex);
gridColumns.push({ headerText: column, key: column });
}
for (i = 1, worksheetRowsCount = worksheet.rows().count() ; i < worksheetRowsCount; i++) {
newRow = {};
row = worksheet.rows(i);
for (columnIndex = 0; columnIndex < columnsNumber; columnIndex++) {
cellValue = row.getCellText(columnIndex);
//newRow[gridColumns[columnIndex].key] = cellValue;
array[row,columnIndex] = cellValue;
}
window.alert(array[0][0]);
data.push(array);
}
</script>
Any help would be greatly appreciated.
Not sure what you're using to parse the Excel, is it IgniteUI ? For what it's worth, the free (community edition) of SheetJS, js-xlsx provides a few functions that produce exactly the output you needed, given the spreadsheet you provided.
The docs are a bit messy, but they are complete, the most interesting sections for this use-case are: Browser file upload form element under Parsing workbooks and XLSX.utils.sheet_to_json. You can run a test with the type of spreadsheet you provided in the code sample below:
$("#input").on("change", function (e) {
var file = e.target.files[0];
// input canceled, return
if (!file) return;
var FR = new FileReader();
FR.onload = function(e) {
var data = new Uint8Array(e.target.result);
var workbook = XLSX.read(data, {type: 'array'});
var firstSheet = workbook.Sheets[workbook.SheetNames[0]];
// header: 1 instructs xlsx to create an 'array of arrays'
var result = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });
// data preview
var output = document.getElementById('result');
output.innerHTML = JSON.stringify(result, null, 2);
};
FR.readAsArrayBuffer(file);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.14.5/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="input" accept=".xls,.xlsx,.ods">
<pre id="result"></pre>
Here the full solution aditionally I've added a group by category function
to demostrante that we can apply functions to the json array.
(async() => {
const url = "./yourfile.xlsx";
const data = await (await fetch(url)).arrayBuffer();
/* data is an ArrayBuffer */
const workbook = XLSX.read(data);
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const sheetValues = XLSX.utils.sheet_to_json(worksheet);
const groupByCategory = sheetValues.reduce((group, product) => {
const { category } = product;
group[category] = group[category] ?? [];
group[category].push(product);
return group;
}, {});
console.log(groupByCategory)
/* DO SOMETHING WITH workbook HERE */
})();
Related
I would like to get the files URL and Name from an array of folders ID, which I'm retrieing with a script.
I would like the result to be placed on the Files tab.
The script I'm using for getting the filesID is the following:
function Folders_URL() {
var ss=SpreadsheetApp.getActive().getSheetByName("Overview");
var fldr=DriveApp.getFolderById("someID");
var files=fldr.getFolders();
var names=[],f,str; while (files.hasNext()) {
f=files.next();
names.push([f.getId(), f.getName()]);
}
ss.getRange(4,2,names.length, 2).setValues(names);
}
For getting files Names with the URL, I use this code.
It would be very helpful if someone can join both codes or just getting the URLs and File names from the array of foldersID
function getFileNames() {
var sheet = SpreadsheetApp.getActive().getSheetByName("someLink");
//Range where the links are set
var links = sheet.getRange("F3:F").getValues();
var filenames = [];
for (var i = 0; i < links.length; i++) {
var url = links[i][0];
if (url != "") {
var filename = SpreadsheetApp.openByUrl(links[i][0]).getName();
filenames.push([filename]);
}
}
//Define cell range
var startRow = 3; // print in row 2 since row 1 is the header row
var fileNameColumn = 4; // Column B = column 2
var destinationCell = sheet.getRange(startRow, fileNameColumn, filenames.length, filenames[0].length);
destinationCell.setValues(filenames);
}
Please be aware that I cannot share any sheet since I have sensitive information from my end.
Thanks!
UPDATED ANSWER
New error when running the script:
Get Urls
I tested this code on my data and it works fine. If it's not working on your data then the problem is your data.
function getUrls() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const osh = ss.getSheetByName("Sheet1");
const vs = sh.getRange(4, 1, sh.getLastRow() - 3, sh.getLastColumn()).getValues();
let obj = { pA: [] };
vs.forEach((r, i) => {
let fldr = DriveApp.getFolderById(r[1]);
let files = fldr.getFilesByName(r[2]);
while (files.hasNext()) {
let file = files.next();
let name = file.getName();;
if (!obj.hasOwnProperty(name)) {
obj[name] = [file.getUrl()];
obj.pA.push(name)
} else {
obj[name].push(file.getUrl())
}
}
});
if (obj.pA.length > 0) {
let o = obj.pA.map(p => [p, obj[p].join('\n')]);
osh.clearContents();
if(o.length > 0) {
osh.getRange(1, 1, o.length, o[0].length).setValues(o);
}
}
}
I am trying to take a template spreadsheet, and copy it, then change the data and export as pdf, finally email it.
So far I can do everything except change it on the sheet. It even changes in the logger. Can someone help with this?
Code as follows:
function After_Submit(e, values){
var range = e.range;
var row = range.getRow(); //get the row of newly added form data
var sheet = range.getSheet(); //get the Sheet
var headers = sheet.getRange(1, 1, 1,5).getValues().flat(); //get the header names from A-O
var data = sheet.getRange(row, 1, 1, headers.length).getValues(); //get the values of newly added form data + formulated values
var values = {}; // create an object
for( var i = 0; i < headers.length; i++ ){
values[headers[i]] = data[0][i]; //add elements to values object and use headers as key
}
const pdfFile = Create_PDF(values);
sendEmail(e.namedValues['Your Email'][0],pdfFile);
}
function sendEmail(email,pdfFile,){
GmailApp.sendEmail(email, "Subject", "Message", {
attachments: [pdfFile],
name: "From Someone"
});
}
function Create_PDF(values, ) {
const PDF_folder = DriveApp.getFolderById("1t_BYHO8CqmKxVIucap_LlE0MhslpT7BO");
const TEMP_FOLDER = DriveApp.getFolderById("1TNeI1HaSwsloOI4KnIfybbWR4u753vVd");
const PDF_Template = DriveApp.getFileById('1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8');
const newTempFile = PDF_Template.makeCopy(TEMP_FOLDER);
const openDoc = SpreadsheetApp.openById(newTempFile.getId());
const newOpenDoc = openDoc.getSheetByName("Sheet1");
var body = newOpenDoc.getDataRange().getValues();
for (const key in values) {
body = body.toString().replace("{{"+key+"}}", values[key]);
}
Logger.log(body);
newOpenDoc.getDataRange().setValues(body);
SpreadsheetApp.flush();
const BLOBPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = PDF_folder.createFile(BLOBPDF);
console.log("The file has been created ");
return pdfFile;
}
When I saw your script, in the for loop of the function Create_PDF, an array retrieved from the sheet is converted to a string. I thought that this might be the reason for your issue.
When this issue was removed, how about the following modification?
From:
var body = newOpenDoc.getDataRange().getValues();
for (const key in values) {
body = body.toString().replace("{{"+key+"}}", values[key]);
}
To:
values = Object.fromEntries(Object.entries(values).map(([k, v]) => [`{{${k}}}`, v]));
var body = newOpenDoc.getDataRange().getValues().map(r => r.map(c => values[c] || c));
By this modification, the converted value by values are put into the sheet.
Reference:
map()
I'm using Google Forms to create an easy method of adding stories and photos to a Google doc for a collective history/journal.
My code takes the Google form responses from the linked Google sheet and then just appends the Google Form responses to the Google doc but I would like to add the responses sorted by the date that gets submitted in the Google form. That way an event that gets submitted that took place on 01/01/2020 will be listed before an event that took place on 01/02/2020 etc.
How would I go about doing that?
function autoFillGoogleDocFromForm(e) {
var timestamp = e.values[0];
var photo = e.values[1];
var date = e.values[2];
var event = e.values[3];
var name = e.values[4];
var photoCap = e.values[6];
var photoDesc = e.values[7];
var fileURL = photo;
var fileID = fileURL.substr(fileURL.search("=")+1); //strip off text before id= in the URL
var image = DriveApp.getFileById(fileID).getBlob();
var doc = DocumentApp.openById("1DrE4ElgaP08uOTH52E2GjgmrJmoL2VZsZ1YlNeV0_20")
var body = doc.getBody();
body.appendPageBreak();
body.appendParagraph(date);
body.appendParagraph(event);
body.appendParagraph(name);
body.appendImage(image);
body.appendParagraph(photoCap);
body.appendParagraph(photoDesc);
doc.saveAndClose();
}
Here is an example of how to insert paragraphs based on date. I use Date object to compare dates so I convert a text string in the form "1/1/2022" to a Date object.
The format of your Doc must have the date string directly following the Page Break.
function testAutoFillGoogleDocFromForm() {
try {
let row = { values: [ "time", "photo", "2/1/2022", "event", "name", "", "photoCap", "photoDesc" ]};
autoFillGoogleDocFromForm(row);
console.log("done");
row = { values: [ "time", "photo", "1/1/2023", "event", "name", "", "photoCap", "photoDesc" ]};
autoFillGoogleDocFromForm(row);
console.log("done");
}
catch(err) {
console.log(err)
}
}
function autoFillGoogleDocFromForm(e) {
try {
let timestamp = e.values[0];
let photo = e.values[1];
let date = new Date(e.values[2]);
let event = e.values[3];
let name = e.values[4];
let photoCap = e.values[6];
let photoDesc = e.values[7];
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let i = 0;
while( i < body.getNumChildren() ) {
let para = body.getChild(i);
if( para.getType() === DocumentApp.ElementType.PARAGRAPH ) {
let j = 0;
console.log("numchild = "+body.getNumChildren());
while( j < para.getNumChildren() ) {
let child = para.getChild(j);
if( child.getType() === DocumentApp.ElementType.PAGE_BREAK ) {
// get next paragraph and check date
if( (i+1) >= body.getNumChildren() ) break; // in case there is a page break at the end of body
para = body.getChild(i+1);
let temp = new Date(para.asParagraph().getText());
console.log(temp);
if( temp > date ) {
body.insertPageBreak(i++);
body.insertParagraph(i++,date.toLocaleDateString());
body.insertParagraph(i++,event);
body.insertParagraph(i++,name);
body.insertParagraph(i++,photoCap);
body.insertParagraph(i++,photoDesc);
return;
}
}
j++;
}
}
i++;
}
// if the date is latest just append a new page
body.appendPageBreak();
body.appendParagraph(date.toLocaleDateString());
body.appendParagraph(event);
body.appendParagraph(name);
//body.appendImage(image);
body.appendParagraph(photoCap);
body.appendParagraph(photoDesc);
}
catch(err) {
console.log(err)
}
}
Could anybody help me out sorting the following code or help me in the right direction?
It needs to import data from a .txt file and store it into localstorage as key & value.
Key is before ':' and value comes after it. A new key / value is separated after each ','.
Sample data from .txt file is:
nl-step1chapter1Question6:U2FsdGVkX19bRT84xShxK+29ypgj1d6ZHt+2DVBCUtY=,nl-step1chapter1Question1:U2FsdGVkX1+/Sv61L69bLvQGTkf1A9Uy4jgJ3KZTkzI=,nl-step1chapter1Question4:U2FsdGVkX1+9SVVOvTKeZuaQGj58L5WnEgL8htS0c7U=,jft:320982da-f32a-46a2-a97c-605ebe305518,nl-step1chapter1Question5:U2FsdGVkX19pi8A+PQZ7rBNCWrFeCwl2HdXpV+wWkFk=,nl-step1chapter1Question2:U2FsdGVkX19hnRnpmP3omzYNU0jXd3NtsHM+mvGYBnc=,nl-step1chapter1Question3:U2FsdGVkX1+hPbMRN+x19y7pF73eXoxG0qK1igZYZbA=
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="application/x-javascript">
$(function importData() {
document.getElementById('file').onchange = function () {
//debugger;
var file = this.files[0];
var reader = new FileReader();
reader.onload = function (progressEvent) {
//console.log(this.result.split(','));
var lines = this.result.split(',');
var list = [];
for (var line = 0; line < lines.length; line++) {
list.push(lines[line]);
localStorage.setItem([line],lines);
}
};
reader.readAsText(file);
};
});
</script>
Any help is much appreciated!
The way you are using FileReader doesn't seem correct to me. This is how your importData() function should be:
$(function importData() {
document.getElementById('file').onchange = function (event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function () {
var text = reader.result;
var lines = text.split(',');
for (var line = 0; line < lines.length; line++) {
let elements = lines[line].split(':');
localStorage.setItem(elements[0], elements[1]);
}
};
reader.readAsText(input.files[0]);
};
});
It will insert the elements in the localStorage as you described. For example: key = step1chapter1Question1 and value = U2FsdGVkX1+/Sv61L69bLvQGTkf1A9Uy4jgJ3KZTkzI=
I followed the demo here:
https://github.com/SheetJS/js-xlsx/tree/master/demos/electron
I'm able to drag an excel file into my electron app.
The documentation says, you can access every cell with:
for(var R = range.s.r; R <= range.e.r; ++R) {
for(var C = range.s.c; C <= range.e.c; ++C) {
var cell_address = {c:C, r:R};
/* if an A1-style address is needed, encode the address */
var cell_ref = XLSX.utils.encode_cell(cell_address);
}
}
How do I use it with my Code below? I got the content of the file stored in my test variable, but I'm not able to access it. The documentation lack of information there.
var do_file = (function() {
return function do_file(files) {
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
data = new Uint8Array(data);
test = XLSX.read(data, {type: 'array'});
console.log(test);
};
reader.readAsArrayBuffer(f);
};
})();
I got no clue how to start with it, thanks in advance
Here it goes:
var do_file = (function() {
return function do_file(files) {
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
data = new Uint8Array(data);
//process_wb(XLSX.read(data, {type: 'array'}));
/* read the file */
var workbook = XLSX.read(data, {type: 'array'}); // parse the file
var sheet = workbook.Sheets[workbook.SheetNames[0]]; // get the first worksheet
/* loop through every cell manually */
var range = XLSX.utils.decode_range(sheet['!ref']); // get the range
for(var R = range.s.r; R <= range.e.r; ++R) {
for(var C = range.s.c; C <= range.e.c; ++C) {
/* find the cell object */
console.log('Row : ' + R);
console.log('Column : ' + C);
var cellref = XLSX.utils.encode_cell({c:C, r:R}); // construct A1 reference for cell
if(!sheet[cellref]) continue; // if cell doesn't exist, move on
var cell = sheet[cellref];
console.log(cell.v);
};
reader.readAsArrayBuffer(f);
};
})();
Generally you can access a cell with standard excel coordinates like
console.log(sheet['My Sheet Name']['B3'].v);
See the full data types here: https://github.com/SheetJS/sheetjs/blob/master/README.md#cell-object