Troubleshoot Google Script for Hiding Columns based on date range - google-apps-script

CORRECTED
I have a google sheet with a table of weekly data (one year's worth).
https://docs.google.com/spreadsheets/d/1u5o0rqEFTiGcZygtbMcohuQAWn6AxfXF78c4Vjp2D8g/edit#gid=84545445
At the top of the sheet I have "to" and "from" date fields.
I am trying to restrict the user's view by hiding all columns which relate to dates outside of the "to"/"from" range.
I have researched how to do this but am clearly unable to craft the script correctly.
UPDATE I have now corrected this but my script will only hide the
first column no matter what dates I have in the "to"/"from" cells.
Script exactly as below.
Thank you.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('NEW - HOURS');
var startColumn = 5;
var values = s.getRange('E7:BD7').getValues();
var start = s.getRange('B4').getValue();
var end = s.getRange('B5').getValue();
var column, len, date, hideCount = 0, showCount = 0;
for (column = values.length - 1; column >= 0; --column) {
date = values[column][0];
if ( typeof date != 'object' || !(date >= start && date < end) ) {
if (showCount) {
s.showColumns(column + startColumn + 1, showCount);
showCount = 0;
}
hideCount++;
} else {
if (hideCount) {
s.hideColumns(column + startColumn + 1, hideCount);
hideCount = 0;
}
showCount++;
}
}
if (showCount) s.showColumns(column + startColumn + 1, showCount);
if (hideCount) s.hideColumns(column + startColumn + 1, hideCount);
}

Mind the row-column notation
Be aware that a range is a 2-dimensional array, where the outer array corresponds to the rows, and the nested array to the columns, as can be withdrawn from the method getRange(row, column).
As a consequence, the values matrix is defined as values[row][column], and values.length will give you the number of rows - for the number of columns use values[0].length.
In summary, you need to change
for (column = values.length - 1; column >= 0; --column) {
date = values[column][0];
...
to
for (column = values[0].length - 1; column >= 0; --column) {
date = values[0][column];
...
ADDITIONAL UPDATE:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('NEW - HOURS');
var startColumn = 5;
var values = s.getRange('E7:7').getValues();

Related

How to Include more sheets to write incremental number

I am using this function which works upon submitting the entry via Google Forms. Currently the below script is adding increment number to the Sheet1 Last empty row
I want to include two more sheets where same incremental number will be added to last empty row.
Column will be same where increment number is pasting that is Column A but Sheet1 data starts from Row2 and Sheet2 and Sheet3 Data starts from row6.
Your help will be much appreciated.
function uPDATEiT() {
var aiColumnName = 'A'; //Sheet1,Sheet2,Sheet3 same column
var requieredColName = 'C' //it is just for Sheet1
var ss = SpreadsheetApp.getActiveSpreadsheet()
var worksheet = ss.getSheetByName('Sheet1','Sheet2','Sheet3')
var aiColRange = worksheet.getRange(aiColumnName + '1:' + aiColumnName + '1000');
var aiCol = aiColRange.getValues();
var aiColIndex = aiColRange.getColumn();
var reqCol = worksheet.getRange(requieredColName + '1:' + requieredColName + '1000').getValues();
var maxSeq = 0;
for (var i = 0; i <= aiCol.length; i++) {
if (parseInt(aiCol[i], 10) > maxSeq) { maxSeq = aiCol[i]; }
}
for (var i = 0; i <= aiCol.length; i++) {
if (('' + reqCol[i]).length > 0 && ('' + aiCol[i]).length === 0) {
maxSeq++;
worksheet.getRange(i + 1, aiColIndex).setValue(maxSeq);
}
}
}
You can use this formula in Sheet2!A4 and Sheet3!A4:
={"ID's";ARRAYFORMULA(IF(B5:B<>"",vlookup(B5:B,{'Data Sheet (Sheet1)'!$B$2:$B,'Data Sheet (Sheet1)'!$A$2:$A},2,false),""))}
What it does?
Check if column B is not empty, then get the id from Sheet1 based on the matched name(column B) as a key in the vlookup()
Output:
Original Sheet2:
Sheet 2 using the formula:
Update
If you just want to append your maxSeq in Sheet2, you can use this:
Code:
function uPDATEiT() {
var aiColumnName = 'A'; //Sheet1,Sheet2,Sheet3 same column
var requieredColName = 'C' //it is just for Sheet1
var ss = SpreadsheetApp.getActiveSpreadsheet()
var worksheet = ss.getSheetByName('Data Sheet (Sheet1)')
var sheet2 = ss.getSheetByName('Sheet2')
Logger.log(worksheet)
Logger.log(sheet2.getLastRow())
var aiColRange = worksheet.getRange(aiColumnName + '1:' + aiColumnName + '1000');
var aiCol = aiColRange.getValues();
var aiColIndex = aiColRange.getColumn();
var reqCol = worksheet.getRange(requieredColName + '1:' + requieredColName + '1000').getValues();
var maxSeq = 0;
for (var i = 0; i <= aiCol.length; i++) {
if (parseInt(aiCol[i], 10) > maxSeq) { maxSeq = aiCol[i]; }
}
for (var i = 0; i <= aiCol.length; i++) {
if (('' + reqCol[i]).length > 0 && ('' + aiCol[i]).length === 0) {
maxSeq++;
worksheet.getRange(i + 1, aiColIndex).setValue(maxSeq);
sheet2.getRange(sheet2.getLastRow()+1,aiColIndex).setValue(maxSeq);
}
}
}
Get the last row in the sheet that contains data using getLastRow() , then increment it by 1.
Output:

Sum up the time values corresponding to same date

In my sheet column A is date and column B is time duration values, I want to find the dates which are repeated and sum up the corresponding time values of the repeated dates and show the sum in the last relevant repeated date. And delete all the other repeated dates. ie if 18/07/2019 is repeated 4 times i have to sum up all the four duration values and display the sum value in the 4th repeated position and delete the first three date 18/07/2019. I have to do this all those dates that are repeated. I have wrote code to my best knowledge
function countDate() {
var data = SpreadsheetApp.getActive();
var sheet = data.getSheetByName("Sheet5");
var lastRow = sheet.getLastRow();
var sh = sheet.getRange('A1:A'+lastRow);
var cell = sh.getValues();
var data= sheet.getRange('B1:B'+lastRow).getValues();
for (var i =0; i < lastRow; ++i){
var count = 0;
var column2 = cell[i][0];
for (var j =0; j < i; j++)
{
var p=0;
var column4 = cell[j][0];
if (column4 - column2 === 0 )
{
var value1 = data[j][0];
var value2 = data[i][0];
var d = value2;
d.setHours(value1.getHours()+value2.getHours()+0);
d.setMinutes(value1.getMinutes()+value2.getMinutes());
sheet.getRange('C'+(i+1)).setValue(d).setNumberFormat("[hh]:mm:ss");
sheet.deleteRow(j+1-p);
p++;
}
}
}
}
The copy of the sheet is shown
column C is the values I obtain through the above code AND column D is the desired value
After computing the sum I need to delete the repeated rows till 15 here
Answer:
You can do this by converting your B-column to a Plain text format and doing some data handling with a JavaScript dictionary.
Code:
function sumThemAllUp() {
var dict = {};
var lastRow = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getLastRow();
var dates = SpreadsheetApp.getActiveSpreadsheet().getRange('A1:A' + lastRow).getValues();
var times = SpreadsheetApp.getActiveSpreadsheet().getRange('B1:B' + lastRow).getValues();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).setNumberFormat("#");
for (var i = 0; i < dates.length; i++) {
if (!dict[dates[i][0]]) {
dict[dates[i][0]] = times[i][0];
}
else {
var temp = dict[dates[i][0]];
var hours = parseInt(temp.split(':')[0]);
var minutes = parseInt(temp.split(':')[1]);
var additionalHours = parseInt(times[i][0].split(':')[0]);
var additionalMinutes = parseInt(times[i][0].split(':')[1]);
var newMinutes = minutes + additionalMinutes;
var newHours = hours + additionalHours;
if (newMinutes > 60) {
newHours = newHours + 1;
newMinutes = newMinutes - 60;
}
dict[dates[i][0]] = newHours + ':' + newMinutes;
}
}
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('A1:B' + lastRow).clear();
var keys = Object.keys(dict);
for (var i = 0; i < keys.length; i++) {
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('A' + (i + 1)).setValue(keys[i]);
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('B' + (i + 1)).setValue(dict[keys[i]]);
}
}
Assumptions I made:
There are a few assumptions I made when writing this, you can edit as needed but I figured I should let you know:
There are only dates in Column A and only times in Column B.
The times in column B are either Hours:Minutes or Minutes:Seconds. Either way, if the value to the right of the : hits 60, it adds one to the left value and resets.
The Sheet within the Spreadsheet is the first sheet; that which is returned by Spreadsheet.getSheets()[0].
References:
w3schools - JavaScript Objects
Spreadsheet.getSheets()
w3schools - JavaScript String split() Method
MDN web docs - parseInt() method
Google Sheets > API v4 - Date and Number Formats

Transpose and Copy to another sheet using Apps script

I need to transpose column data to rows based on the merged header using Apps Script.
Below is the view what would be my input and the expected output,
Input
Output
Sample sheet
What I've written so far:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("A1:AO1");
var mergedValues = [];
//get the header added to the array
mergedValues.push(sheet.getRange("A2:I2").getValues());
Logger.log(mergedValues);
var mergedRanges = range.getMergedRanges();
for (var i = 0; i < mergedRanges.length; i++) {
var calcA1Notation = "A"+(i+3) + ":C"+(i+3);
var monA1Notation = "D"+(i+3) + ":F"+(i+3);
//Load the Transpose values into the array
mergedValues.push([[
sheet.getRange(calcA1Notation).getValues().toString(),
mergedRanges[i].getDisplayValue(),
sheet.getRange(monA1Notation).getValues().toString()
]]);
}
Logger.log(mergedValues[0].length);
for (var i = 0; i < mergedValues.length; i++){
//Writes to the lastrow+1 of the sheet
sheet.getRange(sheet.getLastRow()+1, 1).setValue(mergedValues[i]);
}
}
Can you guys help me in modifying google script to generate the expected result?
The question includes the term "Transpose", but this is misleading.
The goal of the questioner is straight-forward; to copy cells from one sheet to another. With one proviso, to include a column header from one sheet as a cell in the target range.
The questioner demonstrated code though they did not explain to what extent this was purposeful. The code takes three columns of data and concatenates the values into a single cell. At best, one might regard this as an early draft.
The referencing of the source data is uncomplicated; getting the month name is the main complication. I used two loops to work through the rows on the Source sheet because the questioner's intended outcome was that the data should sort by month.
I could have built a routine to convert the month string value to a numeric value, then sorted on that value (I certainly thought about it) - but I didn't;)
The Month names are in UPPERCASE, the questioner's outcome uses TitleCase. Again, I could have built a routine to convert the case, and I did spend some time trying. But in the end I decided that it was not a high priority.
function so5273586002() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Declare the two sheets
var sourcesheet = ss.getSheetByName("Input");
var targetsheet = ss.getSheetByName("Output");
// Get the respective starting row and ending rows.'' the target last row is declared in the loop.
var sourcestartrow = 3;
var targetstartrow = 2;
var sourcelastrow = sourcesheet.getLastRow();
// get the the data
var sourcerange = sourcesheet.getDataRange();
var sourcevalues = sourcerange.getValues();
// rubric for copying data.
// each row of the source must create two rows in the target - one row for each month
// the first three columns are repeats on both rows
// each row includes the source data as well as the month name
// target row #1
// source columns A, B & C to target A,B,C
// Month#1; value in D1 Source=> Target Column D (4)
// source columns DEF to target E F G
// target row #2
// source columns A, B & C to target A,B,C
// Month#2: value in G1 Source=> Target D (4)
// source fields G, H I to target E F G
// the questioner's prefered layout is that all the rows are sorted by month; to achive this, I used two loops
// the first to do the first month; the second to do the second month
for (i = sourcestartrow; i < (sourcelastrow + 1); i++) {
// get the last row for the target
var targetlastrow = targetsheet.getLastRow();
// Columns A, B and C -> Columns A, B and C
var targetRange = targetsheet.getRange(targetlastrow + 1, 1); //target: column =A, row = lastrow plus one
var sourcetest = sourcesheet.getRange(i, 1, 1, 3).copyTo(targetRange); // range = active row, column=A, 1 row, 3 columns, copy to SheetTracker
//Logger.log("source range is "+sourcesheet.getRange(i, 1, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 1).getA1Notation());//DEBUG
// Month Name from the header
var targetRange = targetsheet.getRange(targetlastrow + 1, 4); //target: column =D, (month) row = lastrow plus one
var sourcetest = sourcesheet.getRange(1, 4).copyTo(targetRange, {
contentsOnly: true
}); // range = active row, column=A, 1 row, 3 columns, copy to SheetTracker
// Logger.log("source range is "+sourcesheet.getRange(1, 4).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 4).getA1Notation());//DEBUG
// Month details
// Columns D E and F -> Columns E F and G
var targetRange = targetsheet.getRange(targetlastrow + 1, 5); //target: column =E, row = lastrow plus one
var sourcetest = sourcesheet.getRange(i, 4, 1, 3).copyTo(targetRange, {
contentsOnly: true
}); // range = active row, column=D(4), 1 row, 3 columns, copy to SheetTracker
// Logger.log("source range is "+sourcesheet.getRange(i, 4, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 5).getA1Notation());//DEBUG
} // end loop#1
//Loop#2 to generate rows for the second month
for (i = sourcestartrow; i < (sourcelastrow + 1); i++) {
// get the last row for the target
var targetlastrow = targetsheet.getLastRow();
// Columns A, B and C -> Columns A, B and C
var targetRange = targetsheet.getRange(targetlastrow + 1, 1); //target: column =A, row = lastrow plus one
var sourcetest = sourcesheet.getRange(i, 1, 1, 3).copyTo(targetRange); // range = active row, column=A, 1 row, 3 columns, copy to SheetTracker
//Logger.log("source range is "+sourcesheet.getRange(i, 1, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 1).getA1Notation());//DEBUG
// Month Name from the header
var targetRange = targetsheet.getRange(targetlastrow + 1, 4); //target: column =D, (month) row = lastrow plus one
var sourcetest = sourcesheet.getRange(1, 7).copyTo(targetRange, {
contentsOnly: true
}); // range = active row, column=G, 1 row, 3 columns, copy to SheetTracker
//Logger.log("source range is "+sourcesheet.getRange(1, 7).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 4).getA1Notation());//DEBUG
// Month details
// Columns G H and I -> Columns E F and G
var targetRange = targetsheet.getRange(targetlastrow + 1, 5); //target: column =E, row = lastrow plus one
var sourcetest = sourcesheet.getRange(i, 7, 1, 3).copyTo(targetRange, {
contentsOnly: true
}); // range = active row, column=D(4), 1 row, 3 columns, copy to SheetTracker
// Logger.log("source range is "+sourcesheet.getRange(i, 7, 1, 3).getA1Notation()+", target range is "+targetsheet.getRange(targetlastrow + 1, 5).getA1Notation());//DEBUG
} // end loop#2
}
This screenshot shows the Source sheet ("Input").
These screenshots show the Target sheet ("Output") before and after running the code.
UPDATE
As noted in my comments, the earlier draft lacked two things:
1) it was inefficient and followed poor practices because it wrote the value of each field as it was created. The more appropriate approach would have been to write the data to an array, and then copy the array to the target range when the row-by-row processing was complete.
2) the code consisted of two loops to cater for the 2 months in the demonstration data. However, this is an impractical outcome since it is probable that there will be, in reality, any number of months' data in each row. Again, poor practice, when a more appropriate approach was to assume any number of month's data. The more efficient approach would have been to build an array of data while looping through each row.
This revision overcomes both drawbacks.
In addition, since month names do not sort in any meaningful sequence, I added a numeric month id that can be used for filtering and sorting in the output data sheet.
function so5273586003() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Declare the two sheets
var sourcesheet = ss.getSheetByName("Input");
var targetsheet = ss.getSheetByName("Output");
// Get the respective starting row and ending rows.'' the target last row is declared in the loop.
var targetstartrow = 2;
var sourcestartrow = 2;
var sourcelastrow = sourcesheet.getLastRow();
var sourcelastcolumn = sourcesheet.getLastColumn();
//Logger.log("the last row is "+sourcelastow+", and the last column is "+sourcelastcolumn);
// get the the data
var sourcerange = sourcesheet.getDataRange();
var sourcevalues = sourcerange.getValues();
var sourcelength = sourcevalues.length;
var i = 0;
var m = 0;
var month = 1;
var dataarray = [];
var masterarray = [];
// start loop by row
for (i = sourcestartrow; i < (sourcelastrow); i++) {
// start loop by month (within row)
for (m = 0; m <= (sourcelastcolumn - 6); m = m + 3) {
dataarray = [];
// add first three columns
dataarray.push(sourcevalues[i][0]);
dataarray.push(sourcevalues[i][1]);
dataarray.push(sourcevalues[i][2]);
//add the month name
dataarray.push(sourcevalues[0][3 + m]);
//add month data
dataarray.push(sourcevalues[i][3 + m]);
dataarray.push(sourcevalues[i][4 + m]);
dataarray.push(sourcevalues[i][5 + m]);
//create month id
switch (sourcevalues[0][3 + m]) {
case "JULY":
month = 1;
break;
case "AUGUST":
month = 2;
break;
case "SEPTEMBER":
month = 3;
break;
case "OCTOBER":
month = 4;
break;
case "NOVEMBER":
month = 5;
break;
case "DECEMBER":
month = 6;
break;
case "JANUARY":
month = 7;
break;
case "FEBRUARY":
month = 8;
break;
case "MARCH":
month = 9;
break;
case "APRIL":
month = 10;
break;
case "MAY":
month = 11;
break;
case "JUNE":
month = 12;
break;
default:
month = 100;
break;
} // end switch
// add the month id to the array (used for sorting)
dataarray.push(month);
// add the data to the master array before zeroing for next month
masterarray.push(dataarray);
} // months loop
} // end row loop
// get the length of the master array
var masterlength = masterarray.length;
// define the target range
var TargetRange = targetsheet.getRange(targetstartrow, 1, masterlength, 8);
// set the array values on the Target sheet
TargetRange.setValues(masterarray);
}

Copying or updating the row based on the occurrence of a row cell value

I'm trying to add a given row or update an existing row to a Google Sheet based on a specific value in the row data. My row data is represented as an array object, like this: [id, number, date, type, url, count].
What I expect is that, if there exists a row with a matching number, I increment the count by 1 in the same range in the Google Sheet, else I add a new row with my row data.
Here's what I've tried so far, but it works only for count=2 and not beyond that.
function copyRowBasedOnNumber(sheetId, sheetName, rowData) {
var sheet = SpreadsheetApp.openById(sheetId);
var sheetname = sheet.getSheetByName(sheetName);
var count = 0;
var matchingRow = sheetname.getLastRow();
var values = sheetname.getDataRange().getValues();
for (var row in values) {
for (var col in values[row]) {
if (values[row][col] == rowData[1]) { // rowData[1] corresponds to the number
matchingRow = row;
count++;
break;
}
}
}
if(count == 0) {
var lastRowNum = sheetname.getLastRow();
sheet.insertRowAfter(lastRowNum);
rowData[5] = 1;
sheetname.getRange(lastRowNum + 1, 1, 1, rowData.length).setValues([rowData]);
} else {
sheetname.getRange(parseInt(matchingRow) + 1, 6).setValue(count + 1);
}
return count + 1;
}
I've figured it out. In case this helps someone in future, here's the code. Here, count is the value I want to check before I add a new entry or update an existing entry.
function copyRowToSheetWithoutDuplicate(sheetId, tabName, rowData) {
var sheet = SpreadsheetApp.openById(sheetId);
var sheetname = sheet.getSheetByName(tabName);
var count = 0;
var existingCount = 0;
var existingEntryRow = 0;
var allValues = sheetname.getDataRange().getValues();
var numColumns = sheetname.getLastColumn();
var numRows = sheetname.getLastRow();
for(var i=0;i<numRows;i++) {
var dateFromRowData = rowData[3].substring(0,10);
var todayDate = Utilities.formatDate(new Date(), "GMT+05:30", "''yyyy-MM-dd");
if(allValues[i][1]==rowData[1] && dateFromRowData!=todayDate) {
count++;
existingCount = allValues[i][6];
existingEntryRow = i + 1;
break;
}
}
if(count == 0) {
// insert a row and make an entry
sheetname.insertRowAfter(numRows);
rowData.push(1); // count
sheetname.getRange(numRows + 1, 1, 1, rowData.length).setValues([rowData]);
return 1;
} else {
// update the existing count by incrementing it by 1
existingCount++;
rowData.push(existingCount); // count
sheetname.getRange(existingEntryRow, 1, 1, rowData.length).setValues([rowData]);
return existingCount;
}
}

Find LastRow of column C (when Col A and B have a different row size)?

How to find the last used cell of column C ?
Example: "Sheet1" : "Col A" and "Col B" have 1200 rows. And "Col C" has only 1 row.
## ColA ColB ColC
## 1 1 1
## 2 2 empty
## .. .. ..
## 1200 1200 empty
Here are my unsuccessful tests :
Function find_last_row_other_column() {
var ws_sheet =
var ws = SpreadsheetApp.openById("Dy...spreadsheet_id...4I")
var ws_sheet = ws1.getSheetByName("Sheet1");
var lastRow = ws_sheet.getRange("C").getLastRow();
var lastRow = ws_sheet.getRange("C:C").getLastRow();
var lastRow = ws_sheet.getRange(1,3,ws_sheet.getLastRow()); 1200 rows for colA! instead of row = 1 for col C.
}
Note: I can't use C1 because next time I use the function it will be C1200 or something else.
var lastRow = ws_sheet.getRange("C1").getLastRow();
I ask this because my next goal is to copy/paste the result of C1 into C2:C1200. Here is my test :
var lastRow = ws_sheet.getLastRow();
var target_range = ws_sheet.getRange(1,3,lastRow,1); //C1 until last row
var Formula_values = source_range.getValues();
target_range.setValues(Formula_values);
Thanks in advance ;)
ps: I have spend 2 hours on it. I have tried similar problems & their solutions already given on this website, but I can't happen to make them working. I am lost ! :
More efficient way too look up the last row in a specific column?
and Get last row of specific column function - best solution
As I mentioned in the comments above, this is the subject of the highest score post on StackOverFlow...
The original post returns the value of the last cell in a column but a (very) little modification makes it return the row index.
Original post :
Script:
function lastValue(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + lastRow).getValues();
for (; values[lastRow - 1] == "" && lastRow > 0; lastRow--) {}
return values[lastRow - 1];
}
modified to return index of the last used cell in a column :
function lastValue(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + lastRow).getValues();
for (; values[lastRow - 1] == "" && lastRow > 0; lastRow--) {}
return lastRow;
}
Here is the function to do it:
function lastRowInColumnLetter(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + (lastRow + 1)).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return lastRow + 1;
}
}
and you invoke it as =lastRowInColumnLetter("C").
And here are 3 more useful functions in this context:
function lastValueInColumnLetter(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(column + "1:" + column + (lastRow + 1)).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return values[lastRow];
}
}
function lastValueInColumnNumber(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(1,column,lastRow + 1).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return values[lastRow];
}
}
function lastRowInColumnNumber(column) {
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow() - 1; // values[] array index
var values = SpreadsheetApp.getActiveSheet().getRange(1,column,lastRow + 1).getValues();
while (lastRow > -1 && values[lastRow] == "") {
lastRow--;
}
if (lastRow == -1) {
return "Empty Column";
} else {
return lastRow + 1;
}
}
These functions properly address empty columns, and also start counting backwards from the last row with content on the active sheet getLastRow(), and not from the last row on the sheet (with or without content) getMaxRows() as in the accepted answer.
If you don't have empty cells between your data, you can use this:
function last_Column_Row(){
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var Direction = SpreadsheetApp.Direction;
var xcol = 2;//e.g. for column 2 ("B"), to obtain its last row
var yrow = 8;//e.g. for row 8, to obtain its last column
var lastRow =sheet.getRange(1,xcol).getNextDataCell(Direction.DOWN).getRow();//last row of column 'xcol'
var lastCol =sheet.getRange(yrow,1).getNextDataCell(Direction.NEXT).getColumn();//last column of row 'yrow'
};
It gets the number of next empty cell-1 of a specific row or column (similar to Ctrl + 'arrow' in a sheet)
But If you have empty cells between your data, you can use this:
function last_Row_Column2()
{
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var Direction = SpreadsheetApp.Direction;
var maxR =sheet.getMaxRows();
var maxC = sheet.getMaxColumns();
var yrow = 8;//e.g. for row 8, to obtain its last column
var xcol = 2;//e.g. for column 2 ('B'), to obtain its last row
var valMaxR = sheet.getRange(maxR,xcol).getValue();//for the case that the last row has the last value
var valMaxC = sheet.getRange(yrow,maxC).getValue();//for the case that the last column has the last value
if(valMaxR !=''){var lastRow = maxR;}//if the last row in studied column is the last row of sheet
else{var lastRow =sheet.getRange(maxR,xcol).getNextDataCell(Direction.UP).getRow();}
if(valMaxC !=''){var lastCol = maxC;}//if the last column in studied row is the last column of sheet(e.g.'Z')
else{var lastCol =sheet.getRange(yrow,maxC).getNextDataCell(Direction.PREVIOUS).getColumn();}
};
[UPADTE} Please disregard this answer. User Serge's code instead. I was having a brain fart. His answer is magnitudes better in every way. That will teach me not to answer SO questions after you come back from a cocktail night... [/UPDATE]
The following function will log the last non-blank row number of column C. Note: if, for example, column C has a value in row 1 and row 200, with rows 2-199 blank, the function will return 200 as last non-blank row - it does not account for blank rows above last non-blank row.
function getLastNonBlankColCrow() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastNonBlankColCrow = 0;
for (var i=1, lenRows=sheet.getRange("C:C").getNumRows(); i<=lenRows; i++) {
if ( !sheet.getRange(i, 3).isBlank() ) { // 3 is 1-based index of column C
lastNonBlankColCrow = i;
}
}
Logger.log(lastNonBlankColCrow);
}