Google Apps Script - Cannot read property "5" from undefined - javascript

Trying to hide rows in Google sheet after a task has been marked as closed and then writing a value "1" into a cell, so that the hidden rows can be ignored the next time the function runs.
The code fails on the line,
var status = range[i][5];
When I run the debugger, I can see it has read the correct value from the sheet. It is also showing the correct array of rows to hide.
Any help apprreciated, the rest of the code is below.
function HideClosed() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form responses 5");
var numRows = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var startRow = 2;
var range = sheet.getRange(2, 1, numRows-startRow,lastCol).getValues(); //Get all values except the header rows
var rowsToHide = [];
for (var i = 2; i < numRows; i++){
var status = range[i][5]; // checks column 6 for status
var hidden = range[i][34]; // checks column 35 for hidden status
if (status == "Closed" && hidden != "1") { // checks for Closed jobs //that have not been hidden yet
rowsToHide.push(i+1);
}
}
var L = rowsToHide.length;
for (i = L ; i>0; i--){
sheet.hideRows(rowsToHide[i-1]);
sheet.getRange(rowsToHide[i][34]).setValue("1"); // writes 1 to column 35 after hiding //row
}
}
#Cooper - yeah, that worked, thanks.
It's very slow though, if it didn't time out, it took over 3 mins to hide 15 rows. From reading other threads on here, it seems to be a common enough problem..
Made a few changes to it today to try speed it up, but getting a "Cannot find function hide Rows error" now on the last line. Everything else looks good on the debugger.
function HideClosedWN() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form responses 5");
var numRows = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var startRow = 2;
var range = sheet.getRange(2, 1, numRows-
startRow,lastCol).getValues(); //Get all values except the header rows
var rowsToHide = [];
for (var i=0; i<numRows-startRow; i++){
var status=range[i][5]; // checks column 6 for status
var hidden=range[i][34]; // checks column 35 for hidden status
if (status == "Closed" && hidden == 1){
rowsToHide.push(i+1);
}
}
sheet.getRange(2,1,rowsToHide.length,lastCol).hideRows(2,rowsToHide.length);
}

I think this might run:
function HideClosed()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form responses 5");
var numRows = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var startRow = 2;
var range = sheet.getRange(2, 1, numRows-startRow,lastCol).getValues(); //Get all values except the header rows
var rowsToHide = [];
for (var i=0;i<numRows-startRow-1;i++)
{
var status=range[i][5]; // checks column 6 for status
var hidden=range[i][34]; // checks column 35 for hidden status
if (status=="Closed" && hidden!=1)
{
rowsToHide.push(i+1);
}
}
var L=rowsToHide.length;
for (i=rowsToHide.length-1; i>=0;i--)
{
sheet.hideRows(rowsToHide[i]);
sheet.getRange(rowsToHide[i],35).setValue(1); // writes 1 to column 35 after hiding //row
}
}

Related

Google Sheets Script: Format Row Based on Cell Value - Trying to Optimize Script

I have a script that will loop through the rows of a specific column in my Google Sheet and then format the entire row based on the value that is contained within the cell. The problem is that this script is very slow because I am using getValue on each individual row of the column range, rather than using getValues on the entire column and referencing it like an array.
See the original script below:
function rowLoop() {
var ss = SpreadsheetApp.openById("Fake_ID");
var sheet = SpreadsheetApp.setActiveSheet(ss.getSheetByName("Fake Name"));
var endRow = sheet.getLastRow();
// <= to repeat for all rows
for (var r = 1; r <= endRow; r++) {
rowAlignment(r);
}
}
function rowAlignment(r) {
var sheet = SpreadsheetApp.getActiveSheet();
var c = sheet.getLastColumn();
var row = sheet.getRange(r, 2, 1, c);
// Get cell in column E of row
var typeCell = row.getCell(1,25);
// Get its value
var typeData = typeCell.getValue();
// Test equal to 'Post' with ==
if(typeData == 'Post') {
row.setHorizontalAlignment('right').setFontSize('6').setFontStyle('italic').setFontWeight('normal');
}
else if (typeData == 'Campaign') {
row.setFontWeight('bold').setHorizontalAlignment('left').setFontSize('8').setFontStyle('normal');
}
SpreadsheetApp.flush();
}
The script does exactly what it's meant to, but it's just slow. I tried optimizing it by using getValues rather than getValue. This is what I've written so far, but the issue is that the script doesn't do anything. It doesn't pop any errors, it just doesn't seem to do anything. See below:
function rowTestLoop() {
var ss = SpreadsheetApp.openById("Fake_ID");
var sheet = SpreadsheetApp.setActiveSheet(ss.getSheetByName("Fake_Name"));
var endRow = sheet.getLastRow();
var endCol = sheet.getLastColumn();
var data = sheet.getRange(1,1, endRow, endCol).getValues();
// <= to repeat for all rows
for (var r = 1; r <= endRow; r++) {
var currentRow = sheet.getRange(r, 2, 1, endCol);
if(data[r][24] == 'Post') {
currentRow.setHorizontalAlignment('right').setFontSize('6').setFontStyle('italic').setFontWeight('normal');
}
else if (data[r][24] == 'Campaign') {
currentRow.setHorizontalAlignment('left').setFontSize('8').setFontStyle('normal').setFontWeight('bold');
}
}
SpreadsheetApp.flush();
}
Can anyone please help?
So I believe I have figured it out.
Your are trying to look for tags post and Campaign in column number 24.
However when you extract the sheet data into a array you have account for that fact that array indices start at '0' where as spreadsheet indices start at '1'
Orginal Code: if(data[r][24] == 'Post')
Edited Code: if(data[r-1][23] == 'Post')
So basically you need to modify indices to match that of the array and not the spreadsheet. In other words Row 1 in spreadsheet is element 0 in an array, similarly Row 1, column 12 in the spreadsheet is element[0][11] in an array
function rowTestLoop() {
var ss = SpreadsheetApp.openById("Fake_ID");
var sheet = SpreadsheetApp.setActiveSheet(ss.getSheetByName("Fake_Name"));
var endRow = sheet.getLastRow();
var endCol = sheet.getLastColumn();
var data = sheet.getRange(1,1, endRow, endCol).getValues();
// <= to repeat for all rows
for (var r = 1; r <= endRow; r++) {
var currentRow = sheet.getRange(r, 2, 1, endCol);
if(data[r-1][23] == 'Post') {
currentRow.setHorizontalAlignment('right').setFontSize('6').setFontStyle('italic').setFontWeight('normal');
}
else if (data[r-1][23] == 'Campaign') {
currentRow.setHorizontalAlignment('left').setFontSize('8').setFontStyle('normal').setFontWeight('bold');
}
}
SpreadsheetApp.flush();
}

How to speed up a script to hide rows that meet certain condition

I have the following function that works to search for a column line by line and hides a row when it finds x. It works but is slow.
function SummaryViewGenerate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
for( i=1 ; i<=lastRow ; i++) { // i <= lastRow
var status = sheet.getRange("K"+i).getValue();
if (status == "x") { // status == "x"
sheet.hideRows(i);
}
}
}
The problem is that it is super slow for my use. Any idea on how I can improve it. Someone mentioned on another thread about putting it into an array. Im still a coding newbie so any help in the right direction would be useful.
I think that it will be faster by getValues(). Reference is https://developers.google.com/apps-script/reference/spreadsheet/range#getValues()
Retrieve all data from spreadsheet using getValues. Data is put in 2D array.
Search "x" from the 2D array.
Sample is as follows.
function SummaryViewGenerate(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var array = sheet.getRange('k1').offset(0, 0, sheet.getLastRow(), 1).getValues();
for (var row in array) {
for (var col in array[row]) {
if(array[row][col] == "x") {
sheet.hideRows(row + 1); // (row + 1) is row number.
}
}
}
}

How to pull a different value from google sheets each time it runs

So I am trying to pull the value that is in the latest line of google sheets. Here is the part of the script that I am having trouble with. This script is being written within google scripts.
When this script runs the confirmation email it sends has the value of var cell as undefined. 0
function formSubmitReply(e) {
var userEmail = e.values[3];
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
// Set the status of the new ticket to 'New'.
// Column F is the Status column
sheet.getRange(lastRow, getColIndexByName("Status")).setValue("New");
var supportEmail;
// Calculate how many other 'New' tickets are ahead of this one
var numNew = 0;
for (var i = 2; i < lastRow; i++) {
if (sheet.getRange(i, getColIndexByName("Status")).getValue() == "New") {
numNew++;
}
}
var range = sheet.getRange("B2:B100");
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues()
Logger.log(values[0][0]);
for(n=0;n<values[0].length;++n){
var cell = values[n][1]
}
The problem is here:
for(n=0;n<values[0].length;++n){
var cell = values[n][1]
}
values[0].length is the number of columns while values[n][1] goes through rows.
if you need a loop to go over the rows use this:
for(n=0;n<values.length;++n){
var cell = values[n][1]
}
Otherwise if you have 10 columns and 50 rows, your loop will only go up to ten as values[0].length is 10 and you will pull data from:
A1
A2
A3
A4
and etc until
A10
and then loop will stop.

Google script won't setValue

I'm trying to change the value of a cell in a loop. All variables are defined as they should be but the status itself won't change and returns an undefined or Function not found error.
My code is the following
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2;
var numRows = 3;
var dataRange = sheet.getRange(startRow, 1, numRows, 8)
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var name = row[0];
var name2 = row[1];
var mother = row[2];
var father = row[3];
var sister = row[4];
var grandpa = row[5];
var mail = row[6];
var type = row[7];
var status = row[8];
var done = "DONE";
var not_done = "NOT DONE";
if (status = "TODO") {
//Do something...
status.setValue(done);
}
else {
//Do nothing...
}
}
Heading of the sheet are these:
Name Name2 Mother Father Sister Grandpa Mail Type Status
I tried adding a new loop
for (var i=0; i<numRows; i++) {
Utilities.sleep(500);
sheet.getRange(i+2, 9).setValue("DONE");
SpreadsheetApp.flush();
}
but this resulted in changing contents of other rows to and also an extra loop which shouldn't be necessary.
What (small) thing am I missing here?
That's because you are defining the numColumns to have 8 columns in total while the status field is 9th column (you started counting at 0). Hence it lies outside the range that you defined. In the 2nd one, it sorta worked because your range is 9.
The actual error is that status is a value not a cell object thus it has no "setValue". You should use the debugger which would have told you the exact line and problem. In the 2nd case you are using getRange correctly.also as another answer says you are using different column indexes (8 and 9 respectively)
The simplest way to replicate what you were trying to do is replace:
for (i in data) {
with:
for (var i=0; i<numRows; i++) {
and replace:
status.setValue(done);
with:
sheet.getRange(startRow + i, 9).setValue(done);

Deleting ALL empty rows in a Google Spreadsheet

I've just started using Google Apps script to manage some sheets for a project i'm working on, I am new to Javascript so please go easy if there are any howlers in my code!.
We have and app called forms2mobile that captures data and drops it into a Google spreadsheet. It actually drops different data into different sheets depending on which part of the app you use.
I've hacked together a script that pulls all data from one sheet (source), and drops only certain columns into a second sheet (destination). It then deletes all rows from the source, and any blank rows from the destination.
The problem I have is with deleting blank rows from the destination. Typically the destination will have empty rows at the bottom, and the code I have will only delete empty rows within the range that contains data. So i'm always left with empty rows at the bottom.
The destination sheet will then be used as a data source for forms2mobile, which of course isn't happy with empty rows.
I've found the class getMaxRows() but i'm not sure how to implement it. If anyone could make any suggestions that would be great.
Cheers
Paul
function NEW_copyColumnNumbers( ) {
var spreadsheet_source = SpreadsheetApp.openById('1a89ZIUcy-8168D1damCV3Q9Ix0arQn9jGS6pgp');
var spreadsheet_target = SpreadsheetApp.openById('1GQiLt9utSH_6CV__oJwmcLOkI4E9iNIRPWU7Xr');
var range_input = spreadsheet_source.getRange("A2:CC407");
var range_output = spreadsheet_target.getRange("A"+(spreadsheet_target.getLastRow()+1));
var keep_columns = [66,66,10,11,12,13,14,23,26,31,69,71,74,75,80];
copyColumnNumbers(range_input, range_output, keep_columns);
clearEmptyRows();
clearSourceData();
}
function copyColumnNumbers( range_input, range_output, columns_keep_num ) {
// Create an array of arrays containing the values in the input range.
var range_values = range_input.getValues();
// Loop through each inner array.
for ( var i = 0, row_count = range_values.length; i < row_count; i++ ) {
// Loop through the indices to keep and use these indices to
// select values from the inner array.
for ( j = 0, col_keep_count = columns_keep_num.length; j < col_keep_count; j++ ) {
// Capture the value to keep
var keep_val = range_values[i][columns_keep_num[j]];
// Write the value to the output using the offset method of the output range argument.
range_output.offset(i,j).setValue(keep_val);
}
}
}
function clearEmptyRows() {
var ss = SpreadsheetApp.openById('1GQiLt9utSH_6CV__oJwmcLOkI4E9iNIRPWU7Xr');
var s = ss.getActiveSheet();
var values = s.getDataRange().getValues();
nextLine: for( var i = values.length-1; i >=0; i-- ) {
for( var j = 0; j < values[i].length; j++ )
if( values[i][j] != "" )
continue nextLine;
s.deleteRow(i+1);
}
//I iterate it backwards on purpose, so I do not have to calculate the indexes after a removal
}
function clearSourceData() {
var ss = SpreadsheetApp.openById('1a89ZIUcy-8168D1damCV3Q9Ix0arQn9jGS6pgp');
var s = ss.getActiveSheet();
var data = s.getDataRange().getValues();
for(var n =data.length+1 ; n<0 ; n--){
if(data[n][0]!=''){n++;break}
}
s.deleteRows(2, (s.getLastRow()-1));
}
This is how it works :
function removeEmptyRows(){
var sh = SpreadsheetApp.getActiveSheet();
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
Note : you can handle columns the same way if necessary using getMaxColumn(), getLastColumn() and deleteColumns(number, howMany)
EDIT
by the way, here is also another way to delete empty rows in a spreadsheet... if you combine both it will "clean" your sheet entirely !
function deleteEmptyRows(){
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n].join().replace(/,/g,'')!=''){ targetData.push(data[n])};
Logger.log(data[n].join().replace(/,/g,''))
}
sh.getDataRange().clear();
sh.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
}
Demo sheet in view only - make a copy to use
Script to removeEmptyRows and removeEmptyColumns in Google Sheets. It puts together everything Serge and apptailor mentioned previously. Here is a sample sheet with the script included File > Make a copy... to edit a copy of the sheet. Also a video that shows you how to use this sheet.
//Remove All Empty Columns in the Entire Workbook
function removeEmptyColumns() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxColumns = sheet.getMaxColumns();
var lastColumn = sheet.getLastColumn();
if (maxColumns-lastColumn != 0){
sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn);
}
}
}
//Remove All Empty Rows in the Entire Workbook
function removeEmptyRows() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
if (maxRows-lastRow != 0){
sheet.deleteRows(lastRow+1, maxRows-lastRow);
}
}
}
Just a quick note, I added this "if" statement to keep Serge insas's code from throwing an error if there is no empty bottom row when you are trying to remove empty rows.
Place this if around the last line function removeEmptyRows() and it
will not throw an error:
if (maxRows-lastRow != 0){
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
Removing all empty lines (bottom-up)
before
after
function isEmptyRow(row){
for (var columnIndex = 0; columnIndex < row.length; columnIndex++){
var cell = row[columnIndex];
if (cell){
return false;
}
}
return true;
}
function removeEmptyLines(sheet){
var lastRowIndex = sheet.getLastRow();
var lastColumnIndex = sheet.getLastColumn();
var maxRowIndex = sheet.getMaxRows();
var range = sheet.getRange(1, 1, lastRowIndex, lastColumnIndex);
var data = range.getValues();
sheet.deleteRows(lastRowIndex+1, maxRowIndex-lastRowIndex);
for (var rowIndex = data.length - 1; rowIndex >= 0; rowIndex--){
var row = data[rowIndex];
if (isEmptyRow(row)){
sheet.deleteRow(rowIndex + 1);
}
}
}
function removeEmptyLinesFromAllSheets(){
SpreadsheetApp.getActive().getSheets().forEach(removeEmptyLines);
}
Removing only empty lines from below and above the data
before
after
function isEmptyRow(row){
for (var columnIndex = 0; columnIndex < row.length; columnIndex++){
var cell = row[columnIndex];
if (cell){
return false;
}
}
return true;
}
function getFirstNonBlankRowIndex(data){
for (var rowIndex = 0; rowIndex < data.length; rowIndex++){
var row = data[rowIndex];
if (!isEmptyRow(row)){
return rowIndex;
}
}
return 0;
}
function removePaddedEmptyLines(sheet){
var lastRowIndex = sheet.getLastRow();
var lastColumnIndex = sheet.getLastColumn();
var maxRowIndex = sheet.getMaxRows();
var range = sheet.getRange(1, 1, lastRowIndex, lastColumnIndex);
var data = range.getValues();
var firstRowIndex = getFirstNonBlankRowIndex(data);
sheet.deleteRows(lastRowIndex+1, maxRowIndex-lastRowIndex);
sheet.deleteRows(1, firstRowIndex);
}
function removePaddedEmptyLinesFromAllSheets(){
SpreadsheetApp.getActive().getSheets().forEach(removePaddedEmptyLines);
}
I have tried this piece of code and it works good, you may take a look and try it:
function DeleteBlankRows(){
var sh = SpreadsheetApp.getActiveSheet();
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
for (var Raw = 1; Raw < sh.getLastRow() ; Raw++)
{
if( sh.getRange('A'+Raw).getValue() == '')
{
sh.deleteRow(Raw) //deleteRows(lastRow+1, maxRows-lastRow);
}
}
This works perfectly for me.
function removeEmptyRows(){
var spreadsheet = SpreadsheetApp.openById("IDOFYOURSPREADSHEETFOUNDINURL");
var sh = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
This version allows you to specify top rows you don't want removed and also to ignore columns after ignoreAfterCol in case you don't want some columns considered when you are looking for blanks:
function removeEmptyLines(sheet,ignoreFirstRows,ignoreAfterCol){
sheet=ss.getSheetByName('Sheet12')
//get data and boundaries
var allData = sheet.getRange(1,1,sheet.getMaxRows(),ignoreAfterCol).getValues();
var sheetLength = allData.length;
while(allData[allData.length-1].toString().replace(/,/g,'')=='') allData.pop();
var lastPopulatedRow = allData.length;
//delete empty rows from end
var rowsToDeleteFromEnd = sheetLength - lastPopulatedRow;
if(rowsToDeleteFromEnd > 0) sheet.deleteRows(lastPopulatedRow+1,rowsToDeleteFromEnd);
//iterate through rows and delete blanks one by one
for(var i=lastPopulatedRow-1; i>ignoreFirstRows; i--){
if(allData[i].toString().replace(/,/g,'')=='') sheet.deleteRow(i+1);
}
}
this will help to delete exactly what you want:
Plus point:
you can check as many columns as you want to identify if a row is empty
this will also delete blank rows that contain formula
improve performance: this script deletes directly the empty rows according to their position without iteration through all the rows.
function deleteBlankRows(start_row=4) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//temporarily insert last column to avoid affecting existing data
sheet.insertColumnsAfter(sheet.getMaxColumns(),1);
var lastRow = findLastRow();
var lastCol = sheet.getMaxColumns()
var temp_col = sheet.getRange(start_row,lastCol,lastRow-start_row,1)
//insert formula to show row position if any row is blank from column A to N (can adjust if needed)
sheet.getRange(start_row,lastCol).setFormula('=if(countif(A'+start_row+':N'+start_row+',"*?")=0,row(),0)').copyTo(temp_col)
//get a reversed list of rows position excluded non-empty rows
var rowsPosition = temp_col.getValues().filter(x => x != 0).reverse()
//delete empty rows from bottom to top
rowsPosition.forEach(function(rowPosition){
if (Number(rowPosition) > start_row) {
sheet.deleteRow(Number(rowPosition))
}
})
//finally, delete the temporary column
sheet.deleteColumn(lastCol)
}
function findLastRow() {
const sh = SpreadsheetApp.getActive().getActiveSheet();
const data = sh.getRange("A:L").getValues();
const mR = sh.getMaxRows();
const indexes = [];
data[0].forEach((_, ci) => {
let col = data.map(d => d[ci]);
let first_index = col.reverse().findIndex(r => r != '');
if (first_index != -1) {
let max_row = mR - first_index;
indexes.push(max_row);
}
});
last_row = indexes.length > 0 ? Math.max(...indexes) : 0;
return last_row;
}
function deleteblankRw(){
var sheet=SpreadsheetApp.getActive().getSheetByName('test')
var e=sheet.getRange('A'+sheet.getMaxRows()).getNextDataCell(SpreadsheetApp.Direction.UP).getRow()
for (k=2;k<=e;k++) {
if(sheet.getRange('A'+k).getValue()=='') {
sheet.deleteRow(k);
k=2;e--
if(k==e){break};
SpreadsheetApp.flush();
}
}
}

Categories