Can't read cell values of XLSX generated using SheetJs - javascript

I wrote a JS code where I imported SheetJS and js-xlsx to perform actions on XLSX files (I can't use nodeJs nor npm, bower or any other solution that requires any installation on final user computer).
Shortly, the code has to do the following:
gets data that will be added to excel from user;
imports the excel the user wants to edit;
use a function to determine in which row data has to be added;
save and download a new file with updated data
The problem with the code I wrote is that it works just fine with Excels written, in fact, via Excel, but crashes if a user imports an XLSX that was previously generated and downloaded by my code.
Here's a snippet of the code:
// user choose the source excel
$('#xlf').change(function(e) {
var reader = new FileReader();
reader.readAsArrayBuffer(e.target.files[0]);
reader.onload = function(e) {
var data = new Uint8Array(reader.result);
var wb = XLSX.read(data, {
type: 'array',
cellDates: true,
cellStyles: true,
sheetStubs: true,
raw: true
});
var fSN = wb.SheetNames[0];
var ws = wb.Sheets[fSN];
function findEmpty() {
// this function check all the values in a specific range of cells (C9:C25)
// In order to do so, I included them into an array where I stored the value of the cells;
// later I loop the array and add 1 to the counter *dataSize* anytime I found a value > 0
var dataRange = [ws["C9"].v, ws["C10"].v, ws["C11"].v, , ws["C12"].v, ws["C13"].v, ws["C14"].v, ws["C15"].v, ws["C16"].v, ws["C17"].v, ws["C18"].v, ws["C19"].v, ws["C20"].v, ws["C21"].v, ws["C22"].v, ws["C23"].v, ws["C24"].v, ws["C25"].v];
var dataSize = 0;
var row;
for (i = 0; i < dataRange.length; i++) {
if (dataRange[i] > 0)
dataSize++;
}
row = 8 + dataSize; // 8 is the row of C9 (0 index-based)
return row;
}
var firstEmpty = findEmpty();
var header = ["a", "b", "c", "d", "e", "f"];
//origin is firstEmpty
XLSX.utils.sheet_add_json(ws, [{
a: $('#from').val(),
b: $('#to').val(),
c: $("#differenza").val(),
e: $("#comm").val()
}], {
header: header,
origin: firstEmpty,
skipHeader: true
});
// save file
XLSX.writeFile(wb, "test.xlsx");
}
});
If I try storing in dataRange the cells without the value (.v):
var dataRange = [ws["C9"], ws["C10"], ws["C11"], ws["C12"], ws["C13"], ws["C14"], ws["C15"], ws["C16"], ws["C17"], ws["C18"], ws["C19"], ws["C20"], ws["C21"], ws["C22"], ws["C23"], ws["C24"], ws["C25"]];
it won't crash with neither Excel generated files nor this-code-generated files, however the function findEmpty() will not work as intended (dataSize will be 0, no matter what's inside the cells).
Looks like my code can't write proper cell objects.
My guess is that there's something I should fix with the way I'm saving the file, but after several research and attempts I couldn't figure out a way to fix this - do you have a clue?
Thanks to all

Just in case this could help anyone, I solved my problem changing the IF condition within the FOR loop like this:
if((dataRange[i] !== undefined) && (dataRange[i].v > 0))
dataSize++;
Also, I'm using the dataRange array where I did not pass the .v property
(dataRange = [ws["C9"], ws["C10"], ... , ...])
After few attempts I found my code was not able to read within the .v of a cell of a file it generates, still it write and passes the whole cell object to it

Related

App Script new Google Sheet creation issue

I am trying to write an App Script that takes string data from multiple different spreadsheets (completely separate documents) and puts them all in a new spreadsheet. When I run the logger, it shows me all the data I want. I want each piece of data to show up in Column A, but when I run my script, it only puts 1 data point in the spreadsheet instead of all of them. Can someone give me some guidance? Here is my code:
function pullTogether() {
var files = DriveApp.getFolderById('Folder ID').searchFiles('title != "nothing"');
const rangeName = 'Sheet1!B2:C';
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getId();
const values = Sheets.Spreadsheets.Values.get(name, rangeName).values;
for (const row in values) {
var a1 = (values[row][0]);
Logger.log(a1);
var ss = SpreadsheetApp.openById("ID of new spreadsheet"); //I have the real ID in my code
var cell = ss.getRange("A2");
cell.setValue(a1);
}
}
}
I believe your goal is as follows.
You want to retrieve the values from the column "B" of each Spreadsheet under the specific folder.
You want to put the retrieved values to the column "A" of the destination sheet.
Modification points:
About but when I run my script, it only puts 1 data point in the spreadsheet instead of all of them., when I saw your script, the retrieved value is always put to the cell "A2" of the destination sheet. I think that this might be the reason for your issue.
In your script, I thought that when the following flow is used, the process cost will become low. By this flow, your issue can be also removed.
In your situation, even when Sheets API is not used, the script might work using getValues().
When these points are reflected in your script, it becomes as follows.
Modified script:
Please set the folder ID and the destination Spreadsheet ID.
function pullTogether() {
// Retrieve values from each Spreadsheet.
var values = [];
var files = DriveApp.getFolderById('Folder ID').searchFiles(`title != 'nothing' and mimeType='${MimeType.GOOGLE_SHEETS}'`);
var sheetName = 'Sheet1'
while (files.hasNext()) {
var xFile = files.next();
var sheet = SpreadsheetApp.open(xFile).getSheetByName(sheetName);
if (sheet) {
var v = sheet.getRange("B2:B" + sheet.getLastRow()).getValues();
values = [...values, ...v];
}
}
// Put values to the destination sheet.
var ss = SpreadsheetApp.openById("ID of new spreadsheet"); //I have the real ID in my code
var dstSheet = ss.getSheets()[0];
dstSheet.getRange(2, 1, values.length, values[0].length).setValues(values);
}
Note:
Although I'm not sure about your actual situation, when the above script didn't work by the large data, please modify as follows.
From
dstSheet.getRange(2, 1, values.length, values[0].length).setValues(values);
To
Sheets.Spreadsheets.Values.update({ values }, ss.getId(), `'${dstSheet.getSheetName()}'!A2`, { valueInputOption: "USER_ENTERED" });
References:
getValues()
setValues(values)

Google Sheets, stack report from multiple workbooks

Goal: To stack data from 90+ google workbooks, all with the same sheet name, into the one master sheet for reporting
Info:
All worksheets have the same number of columns.
I have the following script but it does not run properly, I think the issue is with how I am caching / Pushing the data to the array before pasting to the output sheet.
I am trying to build an array then paste it in one go.
The tables I am stacking have 47 columns, unknown number of rows.
The part that opens the sheets is all working perfectly.
// Get the data from the worksheets
var indexsheet = SpreadsheetApp.getActive().getSheetByName("Index");
var outputsheet = SpreadsheetApp.getActive().getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL)
var CurrentCycleName = response.getResponseText()
// Assign datasets to variables
var indexdata = indexsheet.getDataRange().getValues();
// For each workbook in the index sheet, open it and copy the data to a cache
indexdata.forEach(function(row, r) {
try {
//open Entity specific workbook
var workbookid = indexsheet.getRange(r + 1, 7, 1, 1).getValues();
var Entityworkbook = SpreadsheetApp.openById(workbookid)
// Open workhseet
Entitysheet.getSheetByName(CurrentCycleName)
// Add PR Data to cache - stacking for all countrys
var PRDataCache = Entitysheet.getDataRange().push()
} catch {}
})
// Set the all values of the sheet at once
outputsheet.getRange(r + 1, 14).setValue('Issue Splitting Data')
Entitysheet.getRange(2, 1, PRDataCache.length || 1, 47).setValues(PRDataCache)
};
This is the index tab where we are getting the workbookid from to open each file
This is the output file, we are stacking all data from each country
I believe your goal is as follows.
You want to retrieve the Spreadsheet IDs from the column "G" of "Index" sheet.
You want to give the specific sheet name using a dialog.
You want to retrieve all values from the specification sheet in all Spreadsheets. In this case, you want to remove the header row.
You want to put the retrieved values on "Output" sheet.
In this case, how about the following sample script?
Sample script:
function myFunction() {
var ss = SpreadsheetApp.getActive();
var indexsheet = ss.getSheetByName("Index");
var outputsheet = ss.getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
var CurrentCycleName = response.getResponseText();
var ids = indexsheet.getRange("G1:G" + indexsheet.getLastRow()).getValues();
var values = ids.reduce((ar, [id]) => {
try {
var [, ...values] = SpreadsheetApp.openById(id).getSheetByName(CurrentCycleName).getDataRange().getValues();
ar = [...ar, ...values];
} catch (e) {
console.log(`"${id}" was not found.`);
}
return ar;
}, []);
if (values.length == 0) return;
// If the number of columns is different in all Spreadsheets, please use the following script.
// var maxLen = Math.max(...values.map(r => r.length));
// values = values.map(r => r.length < maxLen ? [...r, ...Array(maxLen - r.length).fill("")] : r);
outputsheet.getRange(outputsheet.getLastRow() + 1, 1, values.length, values[1].length).setValues(values);
}
Note:
When the number of Spreadsheet IDs is large, the processing time might be over 6 minutes. I'm worried about this. At that time, how about separating the Spreadsheet IDs?
Reference:
reduce()

Cannot Read Sheet Range From Null

I have a function that allows me to import spreadsheet data from my gmail to Google Sheets. Previously, the spreadsheet data had only had 6 columns to import. Now, some new changes are made and a 7th column was added. After this change was implemented, my function no longer works and Google Sheets throws this error. May I please have some assistance?
So, as I'm looking at this, the intended functionality looks right to me. Skip the first 3 rows (netdata) and take everything below. Could it be the + 1, 1 ?
The Error:
TypeError: Cannot read property 'getRange' of null
My import function:
function importCSVFromGmail() {
var sheetName = "SHEET_NAME"; // Name of sheet tab.
var threads = GmailApp.search("from:EMAIL HERE label:LABEL HERE"); // "from:recipient email here label:name of your filter/label here"
var messages = threads[0].getMessages();
var message = messages[messages.length - 1];
var attachment = message.getAttachments()[0]; // [0] will by default look for only 1 attachment. If there are more than two attachment increase value. ex: [1] , [2]
var data = [];
if (attachment.getContentType() == MimeType.CSV) { // This will look for a CSV file type first
data = Utilities.parseCsv(attachment.getDataAsString(), ",");
} else if (attachment.getContentType() == MimeType.MICROSOFT_EXCEL || attachment.getContentType() == MimeType.MICROSOFT_EXCEL_LEGACY) { // If attachment is an xls, this line will look at the content to determine and convert accordingly.
var tempFile = Drive.Files.insert({title: "temp", mimeType: MimeType.GOOGLE_SHEETS}, attachment).id;
data = SpreadsheetApp.openById(tempFile).getSheets()[0].getDataRange().getValues();
Drive.Files.trash(tempFile);
}
if (data.length > 0) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var netdata = data.slice(3); // This will skip the number of rows starting from the top.
sheet.getRange(sheet.getLastRow() + 1, 1, netdata.length, netdata[0].length).setValues(netdata );
}
}
I'm think the issue is here:
sheet.getRange(sheet.getLastRow() + 1, 1, netdata.length, netdata[0].length).setValues(netdata );
The error message indicates that the issue is with the sheet variable being null. That happens when there is no sheet by the name SHEET_NAME in the spreadsheet.
To fix the error, replace SHEET_NAME with the name of the sheet you want the function to work with. Check for things like leading and trailing whitespace in the sheet's name.

How do I append XLSX data to existing worksheet using SheetJS?

I am currently using NodeJS and the library SheetsJS to read an XLSX workbook.
My task is to take data from multiple sheets and append them to a new worksheet. The structure of the sheet has the Categories in Column A and Category Values in Column B.
*Things I've Tried *
I have tried 2 things that have presented 2 different issues:
1.) I've tried using the builtin function sheet_to_json to format my xlsx data into JSON but it makes the header the key for every object. I've used the option skipHeader: true to negate this but if A1 is the header, A2 is the next value that gets repeated as the object.
Below is a code snippet:
let readFiletoJSON = filename => {
//wb = workbook
let wb = xlsx.readFile(filename, {cellDates: true});
let ws = wb.Sheets["1-Header"]
let currentRange = { s: { c: 0, r: 1 }, e: { c: 1, r: 10 } }
let encodedRange = xlsx.utils.encode_range(currentRange)
let sheetData = []
sheetData.push(
xlsx.utils.sheet_to_json(ws, {range: encodedRange}, {skipHeader: true})
)
console.log(sheetData)
2.) I have also tried creating my own array of objects with Column A as the key and Column B as the value however, I have trouble figuring out how to read the object into the new worksheet. How can I read the values into the new sheet?
I hope this is clear enough. Thank you in advance.

Add a new record to an existing records contained excel file in excel js( already the excel contains some value now trying to insert a new record )

1 -I want to add a new record inside the excel which is already contains some value
2 - Is there any way to use excel as the database for our project
so that client can use the excel effieciently
//script file.js
var Excel = require('exceljs');
var workbook = new Excel.Workbook();
//calling 2 function (writeFile() and writeFile1() )
writeFile();
writeFile1();
// this function should add/ create the record in excel file
function writeFile(){
var worksheet = workbook.addWorksheet('sheet1');
worksheet.columns =[
{header:"Id",key:"id",width:10},
{header:'Type',key:'type',width:15},
{header:'Assigned Engineer',key:'eng',width:25},
{header:'Due Date',key:'ddate',width:18},
{header:'Client Name',key:'cname',width:20},
{header:'person Name',key:'pname',width:20},
{header:'enquiry type',key:'etype',width:18},
{header:'acknowledge',key:'ack',width:20}
]
Worksheet.addRow({id:16,type:"Trading1221",eng:"Dhanasekar122",ddate:new
Date(),cname:"Ford22",pname:"sekar22",etype:"pipeling2",ack:"Y2"})
worksheet.addRow({id:71,type:"Trading3221",eng:"Dhanasekar322",ddate:new
Date(),cname:"Ford32",pname:"sekar32",etype:"pipeling3",ack:"Y3"})
workbook.xlsx.writeFile('file2.xlsx').then(function(){
})
}
//similary this below function should also add the record inside the
// excel
function writeFile1(){
var worksheet = workbook.addWorksheet('sheet1');
worksheet.columns =[
{header:"Id",key:"id",width:10},
{header:'Type',key:'type',width:15},
{header:'Assigned Engineer',key:'eng',width:25},
{header:'Due Date',key:'ddate',width:18},
{header:'Client Name',key:'cname',width:20},
{header:'person Name',key:'pname',width:20},
{header:'enquiry type',key:'etype',width:18},
{header:'acknowledge',key:'ack',width:20}
]
Worksheet.addRow({id:11,type:"Trading1221",eng:"Dhana11sekar122",ddate:new
Date(),cname:"Fo12",pname:"sekar122",etype:"pi1peling2",ack:"Y2"})
worksheet.addRow({id:171,type:"Trading31221",eng:"Dhanasekar11322",ddate:new
Date(),cname:"For1d32",pname:"sek1ar32",etype:"pipelin1g3",ack:"Y13"})
workbook.xlsx.writeFile('file2.xlsx').then(function(){
})
}
// what happening is value is overwriting and the excel has the last
inserted value
I had even tried in the second function of removing the columns but
still works the same and shows error on some time
excelJS requires an array of objects where each object points to row in excel , try doing this , this should solve your pblm
var rows = [{id:11,type:"Trading1221",eng:"Dhana11sekar122",ddate:new Date(),cname:"Fo12",pname:"sekar122",etype:"pi1peling2",ack:"Y2"},
{id:171,type:"Trading31221",eng:"Dhanasekar11322",ddate:new Date(),cname:"For1d32",pname:"sek1ar32",etype:"pipelin1g3",ack:"Y13"}];
worksheet.addRows(rows);
At last i found the solution to the above problem
//file1.js
var Excel = require('exceljs')
var workbook = new Excel.Workbook()
var arr=[]
workbook.xlsx.readFile('./file4.xlsx')
.then(function(){
var worksheet = workbook.getWorksheet(1)
var row =[
[ 55,"trading","sekar",new Date(2017-02-12),"ashok leyaland",arun",
"modeling","Y"],
[99,"training",new Date(2018-02-13),"tata motors","dhana","reference
name","wheldding","Y"]
]
worksheet.addRows(row)
return workbook.xlsx.writeFile('./file4.xlsx')
})
//
first you need to read the respective excel file and then you need to select the particular worksheet of the workbook(excel file) now you can readfile are write file using any of the form you can choose and update the value of the excel in the form of array or arrays
and return the output as file write function

Categories

Resources