Google sheet + Apps script: faster then for loop - google-apps-script

Hi I'm using this code to loop trough another google worksheet find in column 'B' the id number 'idNum' once the id number is found the script replaces the whole row with new data but it seem to be taking a long time before the following script get triggered is there a way to make it faster to loop though or to make the next script trigger faster.
here's my code Thanks
function editRow(){
var mainsheet = ('10_XEaQiR71----- Sheet ID ----uOhi9VVtk5FI')
var tsheet = SpreadsheetApp.openById(mainsheet).getSheetByName('Data')
var targetSheet1 = tsheet.getDataRange().getValues()
var sheet = SpreadsheetApp.getActive();
var sourceData = sheet.getSheetByName('Data Input').getRange('A2:IB2').getValues()[0];
var idNum = sheet.getSheetByName('Data Input').getRange ("B2").getValue();
var copyFrom = sheet.getSheetByName('Data Input').getRange('A2:IB2')
var data = copyFrom.getValues()
for(var i = 0; i<targetSheet1.length;i++){
if(targetSheet1[i][1] == idNum){
var row = i=i+1
tsheet.getRange('A'+row+':IB'+row).setValues(data);
break;
}
}
next script
}

I believe your goal as follows.
You want to reduce the process cost of your script in your question.
Issue and workaround:
In your script, the values are retrieved from getRange("B2") and getRange('A2:IB2'). In this case, I think that you can retrieve them from getRange('A2:IB2').
When you are using V8 runtime, the process cost of for loop is almost the same with others. Ref So in this case, I would like to propose to use TextFinder instead of the for loop. Because I thought that TextFinder is run in the internal server and by this, the search process might be able to be reduced a little. But I'm not sure about your actual situation. So I'm not sure whether this is the correct direction for achieving your issue. So, please test the following script.
When your script is modified, it becomes as follows.
Modified script:
function editRow() {
var mainsheet = '10_XEaQiR71----- Sheet ID ----uOhi9VVtk5FI';
var sheet = SpreadsheetApp.getActive();
var data = sheet.getSheetByName('Data Input').getRange('A2:IB2').getValues();
var idNum = data[0][1];
var tsheet = SpreadsheetApp.openById(mainsheet).getSheetByName('Data');
var row = tsheet.getRange("B1:B" + tsheet.getLastRow()).createTextFinder(idNum).findNext().getRow();
tsheet.getRange(row, 1, 1, data[0].length).setValues(data);
// next script
}
Reference:
Class TextFinder

Related

Is possible to copy a data range from one spreadsheet to another?

function transferDataToDataBase(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var DB = SpreadsheetApp.openById("SHEET ID");
var SCPM_Input = ss.getSheetByName("Report")[0];
var history_Input = DB.getSheetByName(Database);
var blankRow = history_Input.getLastRow();
SCPM_Input.getRange(("A2:T").copyTo(history_Input.getRange(blankRow+1,1,blankRow+1,20));
}
Question:
The last line has an error at ";", don't know how to fix it. It said the syntax error;
It seems copy To only works for transferring from one sheet to another in the same spreadsheet, but not in between spread sheet. Is there a way to copyTo between spreadsheets?
Many thanks.
If you want to copy the formatting as well then copy the entire sheet/tab and then use copyTo() to copy the range and then delete the copied sheet. Or if you just want the data use getValues() and setValues();
This is part of the problem:
var SCPM_Input = ss.getSheetByName("Report")[0]; because the index zero at the end should not be there and you cannot have sheet.getLastRow() + 1 number of rows in a sheet as shown in the next row.
SCPM_Input.getRange(("A2:T").copyTo(history_Input.getRange(blankRow+1,1,blankRow+1,20));
And copyTo doesn't work between spreadsheets.
There is an update:
Since the database will be used by multiple users. I set up a standalone database. I copied my "Report" sheet from spreadsheet 1 to spreadsheet 2. It will create a sheet "copy of Report".I rename, copied to Database sheet and delete it.
Feel free to discuss if there is an easier way to do it. Thanks.
// transfer the report to DataBase Spreadsheet
function transferDataToDataBase(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var db = SpreadsheetApp.openById('spreadsheet ID')
var scpm_Input = ss.getSheetByName("Report");
scpm_Input.copyTo(db);
Utilities.sleep(3000);
db.getSheetByName('copy of report').setName('Import')
var history_Input = db.getSheetByName('Database');
var blankRow = history_Input.getLastRow();
var target = db.getSheetByName('Import');
target.getRange("A2:T").copyTo(history_Input.getRange(blankRow+1,1,blankRow+1,20));
history_Input.getRange(blankRow+1,21).setValue(new Date()).setNumberFormat("yyyy-mm-dd h:mm") //update the date and time
db.deleteSheet(target);
}

"Service Spreadsheets failed while accessing" on (seemingly) simple code

Does anyone have an idea of why my code is throwing the "Service Spreadsheets failed while accessing" error? I've read that this is usually caused by a large dataset, but all the datasets involved here are tiny. For whatever reason, the error is being thrown on line 6. I'm new to both Apps Script and Javascript, but I don't think this code should take very long to run at all. The function simply aims to take values from one sheet and drop them into their corresponding place on another sheet -- I've used formulas in the sheet to find the column number (that is the column_nums var) although ideally I would find an all-script solution. The reason I'm doing this through script and not a simple index match is because I want the values from sheet A to be updated over time and use sheet B to periodically (on a trigger) paste in sheet A's values to track them over time. Apologies if this is a basic question, thanks so much!
function export_maxes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
var new_maxes = source.getRange("E1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
var column_nums = source.getRange("A1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
// find row
var row_num = destination.getRange("B1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getHeight();
var row_num = row_num + 1;
// find columns and input maxes. start at 1 bc data has headers
for (var i = 1; i < new_maxes.length; i++) {
destination.getRange(row_num, column_nums[i]).setValue(new_maxes[i])
}
};
There is ongoing issue tracker for "getDataRegion failed when it faces hidden rows or columns". Alternative solution is to show the hidden column groups by using method:expandAllColumnGroups() and hide the column after you fetched the data by using method:collapseAllColumnGroups()
Your code should look like this.
function export_maxes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
source.expandAllColumnGroups();
var new_maxes = source.getRange("E1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
var column_nums = source.getRange("A1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
// find row
var row_num = destination.getRange("B1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getHeight();
source.collapseAllColumnGroups();
var row_num = row_num + 1;
// find columns and input maxes. start at 1 bc data has headers
for (var i = 1; i < new_maxes.length; i++) {
destination.getRange(row_num, column_nums[i]).setValue(new_maxes[i])
}
};
References:
expandAllColumnGroups
collapseAllColumnGroups

Append new data to sheet without overwriting in GAS

I need to figure out what to add to my script to add new data to the end of my sheet without overwriting the existing data. I'm a total beginner and have researched this, but can't seem to put the code together. Here is my script:
function myFunction() {
var url = "xxxxxxxx"
var response = UrlFetchApp.fetch(url)
var data = response.getContentText()
var result = JSON.parse(data)
var sheet = SpreadsheetApp.getActiveSheet()
sheet.clear()
var headerRow = ['Title','Brand','UPC','In Stock','Stock Level','Price','Seller','Next Day','Ship to Home', 'Link']
sheet.appendRow(headerRow)
for(var i=0; i<result.category_results.length; i++){
var row = [result.category_results[i].product.title, result.category_results[i].product.brand, result.category_results[i].product.upc,result.category_results[i].inventory.in_stock,
result.category_results[i].inventory.stock_level, result.category_results[i].offers.primary.price, result.category_results[i].offers.primary.seller_name,
result.category_results[i].fulfillment.next_day_shipping_eligible, result.category_results[i].fulfillment.ship_to_home, result.category_results[i].product.link]
sheet.appendRow(row)
}
}
Modification points:
In your script, the sheet is cleared every run by sheet.clear(). By this, the existing values are always deleted, and the sheet is overwritten by the new values. I think that this is the reason of your issue.
In your script, appendRow is used in a loop. In this case, the process cost will be high.
In order to append the new values to the sheet, I would like to propose the following flow.
Retrieve values. (This is your script.)
Check whether there are the existing values in the sheet.
Create an array for putting the values to the sheet.
Put the values using the array.
In this case, setValues is used instead of appendRow.
When above points are reflected to your script, it becomes as follows.
Modified script:
function myFunction() {
// 1. Retrieve values. (This is your script.)
var url = "xxxxxxxx";
var response = UrlFetchApp.fetch(url);
var data = response.getContentText();
var result = JSON.parse(data);
// 2. Check whether there are the existing values in the sheet.
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
if (lastRow == 0) {
var headerRow = ['Title','Brand','UPC','In Stock','Stock Level','Price','Seller','Next Day','Ship to Home', 'Link'];
sheet.appendRow(headerRow);
}
// 3. Create an array for putting the values to the sheet.
var values = []
for(var i=0; i<result.category_results.length; i++){
var row = [result.category_results[i].product.title, result.category_results[i].product.brand, result.category_results[i].product.upc,result.category_results[i].inventory.in_stock,
result.category_results[i].inventory.stock_level, result.category_results[i].offers.primary.price, result.category_results[i].offers.primary.seller_name,
result.category_results[i].fulfillment.next_day_shipping_eligible, result.category_results[i].fulfillment.ship_to_home, result.category_results[i].product.link];
values.push(row);
}
// 4. Put the values using the array.
sheet.getRange(lastRow + 1, 1, values.length, values[0].length).setValues(values);
}
Note:
In this modified script, it supposes that your values can be retrieved with row. Please be careful this.
References:
setValues(values)
Benchmark: Reading and Writing Spreadsheet using Google Apps Script

Google app script trigger not writing data to other sheets in same spreadsheet

I have the following app script associated with a Google Spreadsheet that is accepting data from a Google Form:
function writePatientData() {
var spreadsheet = SpreadsheetApp.openById("<spreadsheet id>");
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
//get last row in active/main sheet
var numRows = sheet.getLastRow();
//get last row of data
var last_row = sheet.getSheetValues(numRows, 1, 1, 23);
//get patientID (column V) in last row of sheet
var lastPatientID = sheet.getRange(numRows,3).getValue();
//find patient sheet based on patientID and make it active, then write to it
var patientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
var activePatientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
activePatientSheet.getRange(activePatientSheet.getLastRow()+1, 1,1,23).setValues(last_row);
}
What this script is doing is writing data (a row) to another sheet within this spreadsheet based on the the patientID (column V). This works as it should when I manually run the script. However, when I set a trigger to run this script (either onSubmit or edit) nothing happens. I created another function that just writes a message to the logs and set a trigger for that function and it works, so I think there is something in the script that is causing it to fail. Any ideas appreciated.
There are a few issues with your code. I tried to fix it while commenting each line I changed. Hopefully that is clear enough, please comment if you have any questions and I'll try to clarify.
function writePatientData() {
var spreadsheet = SpreadsheetApp.getActive(); //no need for id if the script is on the same spreadsheet
//var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
//setActiveSheet will not work from a trigger like on-form-submit (what if no-one has the sheet open, or multiple have)
var sheet = spreadsheet.getSheets()[0]; //if you want the first sheet, just get it, no need to "activate"
var numRows = sheet.getLastRow();
var last_row = sheet.getSheetValues(numRows, 1, 1, 23)[0]; //added [0] since it is just one row
//var lastPatientID = sheet.getRange(numRows,3).getValue(); //you already have this in memory
var lastPatientID = last_row[2]; //arrays are zero based, that's why 2 instead of 3
//btw, you mention column V, but this is actually C
//var patientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
//you already have the spreadsheet, no need to get it again
var patientSheet = spreadsheet.getSheetByName(lastPatientID);
//var activePatientSheet = spreadsheet.getSheetByName(lastPatientID); //this is the exact same as above, why?
patientSheet.appendRow(last_row); //appendRow is just simpler than getRange(getLastRow).setValues
}

Improving Apps Script flexibility by using a column of sheet data instead of hard-coded IDs

Background: My coworkers originally each had a worksheet within the same Google Sheets file that makes a lot of calculations (and was getting unusable). Now, everyone has their own (known) Google Sheets file. To run the same calculations, we need to consolidate all that data into a master sheet (image ref below). We tried =importrange(...), but it's too heavy and breaks often (i.e., Loading... and other unfilled cells).
I've written some code to do this import, but right now its only manual: manually repeating the code and manually add the sheet IDs and changing the destrange.getRange(Cell range) each time. We have 80+ analysts, and fairly high turnover rates, so this would take an absurd amount of time. I'm new to Sheets and Apps Script, and know how to make the script use a cell as reference for a valid range or a valid ID, but I need something that can move a cell down and reference the new info.
Example:
Sheet 1 has a column of everyone Sheet ID
Script Pseudocode
get first row's id(Row 1), get sheet tab, get range, copies to active sheet's corresponding row(Row 1).
gets second row's id(Row 2), get sheet tab, get range, copies to active sheet's corresponding row (Row 2)
etc.
My script understanding is way to low to know how to process this. I have no idea what to read and learn to make it work properly.
function getdata() {
var confirm = Browser.msgBox('Preparing to draw data','Draw the data like your french girls?', Browser.Buttons.YES_NO);
if(confirm == 'yes'){
// I eventually want this to draw the ID from Column A:A, not hard-coded
var sourcess = SpreadsheetApp.openById('1B9sA5J-Jx0kBLuzP5vZ3LZcSw4CN9sS6A_mSbR9b26g');
var sourcesheet = sourcess.getSheetByName('Data Draw'); // source sheet name
var sourcerange = sourcesheet.getRange('E4:DU4'); // range
var sourcevalues = sourcerange.getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet(); //
var destsheet = ss.getSheetByName('Master Totals'); //
// This range needs to somehow move one down after each time it pastes a row in.
var destrange = destsheet.getRange('E4:DU4');
destrange.setValues(sourcevalues); // Data into destsheet
}
}
Any suggestions are greatly appreciated!
Thanks to tehhowch for pointing me in the right direction!
function getdata() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master Totals');
var confirm = Browser.msgBox('Drawing Data','Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO);
if(confirm =='yes'){
var lr = ss.getLastRow();
for (var i = 4; i<=lr; i++) {
var currentID = ss.getRange(i, 1).getValue();
var sourcess = SpreadsheetApp.openByUrl(currentID);
var sourcesheet = sourcess.getSheetByName('Data Draw');
var sourcerange = sourcesheet.getRange('E4:DU4');
var sourcevalues = sourcerange.getValues();
var destrange = destsheet.getRange('E' +i+':'+ 'DU'+ i);
destrange.setValues(sourcevalues);
I just had to learn how to use a variable loop.
Edit: thanks also to Phil for making my question more presentable!
Now that you've figured out one way to do it, I'll offer an alternative that uses batch methods (i.e. is much more time- and resource-efficient):
function getData() {
var wb = SpreadsheetApp.getActive();
var ss = wb.getActiveSheet();
var dest = wb.getSheetByName('Master Totals');
if (!dest || "yes" !== Browser.msgBox('Drawing Data', 'Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO))
return;
// Batch-read the first column into an array of arrays of values.
var ssids = ss.getSheetValues(4, 1, ss.getLastRow() - 4, 1);
var output = [];
for (var row = 0; row < ssids.length; ++row) {
var targetID = ssids[row][0];
// Open the remote sheet (consider using try-catch
// and adding error handling).
var remote = SpreadsheetApp.openById(targetID);
var source = remote.getSheetByName("Data Draw");
var toImport = source.getRange("E4:DU4").getValues();
// Add this 2D array to the end of our 2D output.
output = [].concat(output, toImport);
}
// Write collected data, if any, anchored from E4.
if(output.length > 0 && output[0].length > 0)
dest.getRange(4, 5, output.length, output[0].length).setValues(output);
}
Each call to getRange and setValues adds measurable time to the execution time - i.e. on the order of hundreds of milliseconds. Minimizing use of the Google interface classes and sticking to JavaScript wherever possible will dramatically improve your scripts' responsiveness.