How to set range as variable based on changing height of data - google-apps-script

The code below takes the same range of data from multiple files in a folder. However, it only uses data in that range if it meets a criteria. So, the returned values from each file vary in height.
I have put a ??? indicating where I need the height of the range I am writing that data to to change for each loop based on the height of the data I am collecting from each file.
function getAllData() {
var folder = DocsList.getFolderById("folderid");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base")
sheet.clearContents();
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet 1");
var lastLine = sheet2.getLastRow();
var values = sheet2.getRange('A3:J').getValues();
var formulas = sheet2.getRange('A3:J').getFormulas();
var data = [];
for(var row = 0 ; row < (values).length ; row++){
var lastrow = sheet.getLastRow()+1;
if (values[row][0] != '') {
for(var col = 0 ; col < formulas[row].length ; col++){
if(formulas[row][col] != '')
{values[row][col] = formulas[row][col]};
data.push(values[row]);}
if(data.length > 0)
sheet.getRange(lastrow, 1, ???, data[0].length).setValues(data);
}
}
};
}}

There's only 1 valid value in the "???", and it is "data.length", do you really want to setValues() everytime in the loop? Shouldn't it be better to build all the data then output all at once?

Related

Script exceeds maximum execution time

I am using a script to first copy a list of all terminated products from "data" tab of the sheet to the "terminated tab"
The data tab looks like below
The code checks if there is an end date written
if it is - the row is copied and pasted in the "terminated" tab
Once all rows (around 2000) are completed
the code the deletes all rows from the "data" tab that have an end date on it
But the code is not very efficient and data is huge - I get a "maximum execution time exceeded" error
function movingTerms() {
var app = SpreadsheetApp ;
var sheet1 = app.getActiveSpreadsheet().getSheetByName("data") ;
var sheet3 = app.getActiveSpreadsheet().getSheetByName("Terminations");
var range1 = sheet1.getRange(2, 1, sheet1.getLastRow() - 1,9);
var range3 = sheet3.getRange(2, 1, sheet3.getLastRow(), 9);
var values1 = range1.getValues();
var values3 = range3.getValues();
var rowcount = sheet1.getLastRow();
var row_deleted = 0;
for (var i = 0; i < (sheet1.getLastRow() - 1); i++)
{
if (values1[i][4] !== "")
{
var rowtodelete = sheet1.getRange(i + 2, 1, 1, 10);
var rowtoadd = sheet3.getRange(sheet3.getLastRow() + 1, 1);
rowtodelete.copyTo(rowtoadd);
}
}
for (var k = 0; k < values1.length; k++)
{
var row = k + 1 - row_deleted;
if (values1[k][4] !== "")
{
var getridof = row +1;
sheet1.deleteRow(getridof);
row_deleted++;
}
}
}
I generally like to see the spreadsheet to do this correctly but this is the way that I would do it.
function movingTerms() {
var ss=SpreadsheetApp.getActive();
var sheet1=ss.getSheetByName("data") ;
var sheet3=ss.getSheetByName("Terminations");
var range1=sheet1.getRange(2, 1, sheet1.getLastRow()-1,9);
var range3=sheet3.getRange(2, 1, sheet3.getLastRow(),9);//You don't really need this
var values1=range1.getValues();
var values3=range3.getValues();//You don't really need this
var rowcount=sheet1.getLastRow();
var rowsdeleted = 0;
for (var i=0;i<values1.length;i++) {
if (values1[i][4]!="") {//column5
var rowtodelete = sheet1.getRange(i-rowsdeleted+2, 1, 1, 10);
var rowtoadd = sheet3.getRange(sheet3.getLastRow()+1,1);//You could also append to sheet 3 if you wish
rowtodelete.copyTo(rowtoadd);
sheet1.deleteRow(i-rowsdeleted+2);
rowsdeleted++;
}
}
}

Apps Script to Delete Row from Google Sheet if Match with Cell Value?

I am working for my script to delete any rows in the POHistory tab of my Google Sheet file that have a matching value in their first cell when compared to the value found in Cell E9 of my POTemplate tab. I am not seeing any response when executing the command, though. Not sure what is missing here. I, essentially, am getting an error message stating the rows I am trying to delete are "out of bounds". Thank you for looking.
function deleteOld() {
var app = SpreadsheetApp;
var orderSheet = app.getActiveSpreadsheet().getSheetByName("POTemplate");
var historySheet = app.getActiveSpreadsheet().getSheetByName("POHistory");
var poNO = orderSheet.getRange("E9").getValue();
var lastRow = historySheet.getLastRow();
var myRange = historySheet.getRange("A2:A" + lastRow);
var data = myRange.getValues();
for(i = 0; i < data.length; i++){
if(data[i][0] != poNO){
continue;
}else{
historySheet.deleteRow(i);
}}}
Because you're deleting a row, the i value no longer corresponds to the same range. For example, you have an array with 5 elements:
[A, B, C, D, E]
If you delete element 0, you'll get
[B, C, D, E]
Now you increment your iterator so i++; // i = 1 and so the next value that you'll operate on is not "B", but instead "C".
You can try yourself by running
function test() {
var letters = ["A", "B", "C", "D", "E"];
for (var i=0; i<letters.length; i++) {
Logger.log("i : " + i + " || Letter: " + letters[i]);
letters.shift();
}
}
To fix your code, add a row variable that is independent of your array iterator i.
Try this as a template:
function deleteOld() {
var app = SpreadsheetApp;
var orderSheet = app.getActiveSpreadsheet().getSheetByName("POTemplate");
var historySheet = app.getActiveSpreadsheet().getSheetByName("POHistory");
var poNO = orderSheet.getRange("E9").getValue();
var lastRow = historySheet.getLastRow();
var myRange = historySheet.getRange("A2:A" + lastRow);
var data = myRange.getValues();
var row = 1;
for(i = 0; i < data.length; i++) {
if(data[i][0] != poNO){
row++; // Go to the next row
continue;
} else {
historySheet.deleteRow(row);
row--; // Deleted a row
}
}
}

Google Sheets Script to Fill Down intermittent blanks in a column

Column C has an ID in it that pertains to several rows, but only the first row has an ID in it.
I need to copy that ID value to the blank cells beneath, until I hit a cell that has another value in it.
I have tried adapting this script but it hits a timeout error.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var last = sheet.getLastRow();//how many times should I do this?
for (i = 5528; i < last; i++) {
var test = sheet.getRange(i, 1,1,1);
//Logger.log(test);
//looks to see if the cell is empty
if (test.isBlank()) {
var rewind = sheet.getRange(i-1, 1, 1, 1).getValues();//gets values from the row above
sheet.getRange(i, 1, 1, 1).setValues(rewind);//sets the current range to the row above
}
}
}
i is set to a big number because every time it times out I have to start over!
I have read that it would be better to bring in the column in an array, work on it, then put it back out to save a lot of time.
I have tried to adapt this but can't get past the variable.
Am I on the right track? I would like to pretty up a solution for the future where I can pass a column or range and do the same thing.
Here is my failing attempt:
function FillDown2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet2");
var tracts = sheet.getRange("C15:C").getValues();
var allTractList = [];
var title;
for (var row = 0, var len = tracts.length; row < len; row++) {
if (tracts[row][0] != '') {
//response = UrlFetchApp.fetch(tracts[row]);
//doc = Xml.parse(response.getContentText(),true);
title = tracts[row][0];
//newValues.push([title]);
allTractList.push([title]);
Logger.log(title);
} else allTractList.push([title]);
}
//Logger.log('newValues ' + newValues);
Logger.log('allTractList ' + allTractList);
// SET NEW COLUMN VALUES ALL AT ONCE!
sheet.getRange("B15").offset(0, 0, allTractList.length).setValues(allTractList);
return allTractList;
}
Holy Smokes! I did it!
Not sure about why error happened but I had made some changes and got it to work!
Hope this is helpful to others:
function FillDown2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet2");
var tracts = sheet.getRange("C15:C").getValues();
var allTractList = [];
var title;
var len = tracts.length;
for (var row = 0; row < len; row++) {
if (tracts[row] != '') {
//response = UrlFetchApp.fetch(tracts[row]);
//doc = Xml.parse(response.getContentText(),true);
title = tracts[row];
//newValues.push([title]);
allTractList.push([title]);
Logger.log(title);
} else allTractList.push([title]);
}
//Logger.log('newValues ' + newValues);
Logger.log('allTractList ' + allTractList);
// SET NEW COLUMN VALUES ALL AT ONCE!
sheet.getRange("B15").offset(0, 0, allTractList.length).setValues(allTractList);
return allTractList;
}
Credit to Bryan here:

Delete Duplicate Row in Google Spreadsheet

I need this script to delete the row (within the Registration sheet) with the matching Registration Code to the Cancel Registration sheet's Registration code. As of now, this script only deletes a row if "sheetR.deleteRow(i);" is not inside "if (regCodeR === regCodeCR) {}". It doesn't delete the correct row either.
function rD() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetR = ss.getSheetByName("Registration");
var sheetCR = ss.getSheetByName("Cancel Registration")
var dataR = sheetR.getDataRange().getValues();
var dataCR = sheetCR.getDataRange().getValues();
var headerRow = 1;
for (var i = 1; i in dataR && i in dataCR; ++i) {
var rowR = dataR[i];
var rowCR = dataCR[i];
var duplicate = false;
var regCodeR = sheetR.getRange(headerRow + i, 10).getValues();
var regCodeCR = sheetCR.getRange(headerRow + i, 9).getValues();
if (rowR[9] === rowCR[8]) {
duplicate = true;
}
}
if (regCodeR === regCodeCR) {
sheetR.deleteRow(i);
}
}
I tried this code with simple data having one column with registration numbers. You can modify the main sheet range according to your data. Also make sure to change the sheet names.
Tried and tested the code below :
function deleteDup(){
var mainSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('regis');
var cancelSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('cancel');
var mainSheetValues = mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).getValues();
var cancelSheetValues = cancelSheet.getRange(2, 1, cancelSheet.getLastRow(),1).getValues();
var k = 0;
var del = [];
// Getting the row index of matching values
for (i = 0; i < cancelSheetValues.length; i++)
{
var cancelValue = cancelSheetValues[i][0];
Logger.log(cancelValue);
for (j = 0; j < mainSheetValues.length; j++)
{
if(mainSheetValues[j][0] == cancelValue){
del[k] = j;
k++;
}
}
}
del.sort();
var count =0;
// deleting the values from the main sheet values array
for (i = 0; i < k; i++ )
{
mainSheetValues.splice((del[i] - count), 1);
count++;
}
var len = mainSheetValues.length;
// Update the sheet with new values
mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).clearContent();
mainSheet.getRange(2, 1, len,1).setValues(mainSheetValues);
}
But instead you can also do this way:
Set a flag value in the registration sheet if it matches with the cancellation sheet.
Then loop through the registration sheet data and delete the matching flag row.
Hope that helps!

optimizing code and how to take selected region of active spreadsheet as input-google docs

I have written script for changing a format like 12.4/12/12.03 into 12:40:00/12:00:00/12:03:00
Here's the code:
function myFunction() {
var sheet=SpreadsheetApp.getActiveSheet();
var rows= sheet.getDataRange();
var numRows=rows.getNumRows();
var values=rows.getValues()
var column = [];
var p = 0;
var k = "H";
for (var i=0;i<numRows;i++) {
// var cell =
//Split the string a the .
var string = values[i][7].split(".");
string[0] = string[0].toString();
p = i+1;
k = "H"+p;
var cell = sheet.getRange(k);
if(string[1]){
string[1] = string[1].toString();
// if the second part is like 4 in 12.4 you set it to 40
if(string[1]!=0) {
if (string[1].length == 1 )
{ string[1] += "0";}
}
// Set the row value to the format you like, here : 12:40:00/12:40
var changed_format = string[0] + ":" + string[1] + ":00";
values[i][7]=changed_format;
p = i+1;
k = "H"+p;
cell.setValue(changed_format);
}
else {
var changed_format = values[i][7]+":00:00";
cell.setValue(changed_format);
}
}
In the above code, I have mentioned columns...i.e., I have to run this script for each column...every time... ex: values[i][7] k="H"+p for 8th column. So, can anyone plz tell me how to do...all at a time...and if possible reduce my code..(optimize)..and also..if is it possible to do like this : if I select the column in the spreadsheet and the changes done by the script applies to that selected region...I mean I want my script to take the selected region as input...is it possible to do..if how.?
One key to optimize your code is to reduce the number of calls to Google services and try getting them done using JavaScript. Here is an optimized version that you could use.
Note that I havent tested it - so if you come across minor syntax errors, feel free to fix them or give a shout if you cannot fix them.
function myFunction() {
var sheet=SpreadsheetApp.getActiveSheet();
var rows= sheet.getDataRange();
var values=rows.getValues();
var COL_H = 8;
var numRows=values.length ; // Length of array = nuymRows. rows.getNumRows();
var column = [];
//var p = 0;
//var k = "H";
var destArray = new Array();
for (var i=0;i<numRows;i++) {
// var cell =
//Split the string a the .
var string = values[i][7].split(".");
string[0] = string[0].toString();
//p = i+1;
//k = "H"+p;
//var cell = sheet.getRange(k);
if(string[1]){
string[1] = string[1].toString();
// if the second part is like 4 in 12.4 you set it to 40
if(string[1]!=0) {
if (string[1].length == 1 )
{ string[1] += "0";}
}
// Set the row value to the format you like, here : 12:40:00/12:40
var changed_format = string[0] + ":" + string[1] + ":00";
//values[i][7]=changed_format;
//p = i+1;
//k = "H"+p;
//cell.setValue(changed_format);
destArray.push([changed_format]);
}
else {
var changed_format = values[i][7]+":00:00";
//values [i][7] = changed_format;
//cell.setValue(changed_format);
destArray.push([changed_format]);
}
}
var destRange = sheet.getRange(1, COL_H, destArray.length, 1);
destRange.setValues(values);
}
TIP: Putting formatted code in the question helps readability