Go to last line of data in Google Sheets - google-apps-script

I have poked around and found the following code that will advance to the last line on data in our Google Spreadsheet- at least until we add more lines beyond row 297.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getSheets()[0].getRange("B297");
SpreadsheetApp.setActiveRange(range);
}
I am trying to figure out how to write this script so that it will go to the last line of data, regardless of the line number.
Is there a change that I can make to this code to accomplish this?

The method getLastRow() tells you the last row of a sheet that has data. This can be used to move the selection to that row. Another thing I changed in the sheet selection: your script always operates on the first sheet; it makes more sense to operate on whichever sheet is active currently.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(sheet.getLastRow(), 1);
SpreadsheetApp.setActiveRange(range);
}
This can be placed into the spreadsheet menu using onOpen.
By the way, pressing Ctrl + ArrowDown does the same thing, if you do it in a column that has some data in every row (like the date or Id column).

The script below allows you to go to the last cell with the content of column A. It works even if some cells in the column A contain formulas.
Modifying the number in parentheses in lastRowOfCol(1) allows you to reach the last cell with content from another column.
Additionally, you can also change the focus to the first empty cell after the last one with content.
function onOpen(){
lastRowOfCol(1); //Enter the column number you want to use as the base of the search
}
function lastRowOfCol(column){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var total = sheet.getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(1,column,total).getValues();
for(var i=total-1;values[i]=="" && i>0; i--){}
var last = sheet.getRange(i+1,column);
//var last = sheet.getRange(i+1,1); //Option to fetch the last row of a given column, but to position in column 1
//var last = sheet.getRange(i+2,column); //Option to go to the cell below the last one with content
sheet.setActiveSelection(last);
}
Script from Marcelo Camargo in this forum

The currently relevant option that works on all non-hidden sheets, and not just on the active one:
function onOpen() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheets = ss.getSheets()
const properties = PropertiesService.getDocumentProperties()
const lastActiveSheetName = properties.getProperty("lastActiveSheetName")
let lastActiveSheet
for (let sheet of sheets) {
if (!sheet.isSheetHidden()) {
const sheetName = sheet.getName()
const lastEdit = properties.getProperty(sheetName)
if (lastEdit) {
if (sheetName !== lastActiveSheetName){
sheet.getLastRow() // Without this magic does not work - I could not figure out the reasons
sheet.getLastColumn() // Without this magic does not work - I could not figure out the reasons
const [lastRow, lastCol] = lastEdit.split(',')
sheet.getRange(Number(lastRow), Number(lastCol)).activate() // With focus set to this cell
//sheet.setActiveSelection(sheet.getRange(Number(lastRow), Number(lastCol))) // Without setting focus to this cell
}
else {
lastActiveSheet = sheet
}
}
}
}
if(lastActiveSheet){
lastActiveSheet.getLastRow()
lastActiveSheet.getLastColumn()
const [lastRow, lastCol] = properties.getProperty(lastActiveSheetName).split(',')
lastActiveSheet.getRange(Number(lastRow), Number(lastCol)).activate()
}
}
function onEdit() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheet = ss.getActiveSheet()
if (!sheet.isSheetHidden()) {
const cell = ss.getActiveCell()
const row = cell.getRow()
const column = cell.getColumn()
if (row !== 1 || column !== 1) { // Protection from the evil magic of "self-editing" the first cell
const sheetName = sheet.getName()
PropertiesService.getDocumentProperties().setProperty(sheetName, `${row},${column}`)
PropertiesService.getDocumentProperties().setProperty("lastActiveSheetName", sheetName)
}
}
}
PS: Please note that in the code I do not use a semicolon separator - it's more convenient for me.

Related

Automatically hide rows with empty cells in predefined column - only hides non-empty cells

I currently have the following script but in contrast to my expectations it only hides the rows with non-empty cells in column F and keeps the one with empty cells unhidden...what is wrong? ==="" is correct right?
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var range = sheet.getRange(6,6,274,1);
function onOpen() {
{
//get the values to those rows
var values = range.getValues();
//go through every row
for (var i=0; i<values.length; i++){
//if row value is equal to empty
if(values[i][0] === ""){
//hide that row
sheet.hideRows(i+1);
}
}
}
Also, is there a way to do this whole thing in multiple sheets but with different ranges without having to completely multiplying the whole script with different variables? I am quite new to AppScript and the "for every i" is still something I do not understand how it works...Thank you so much for your help!
Edit: Can I also add another
if(values[i][0] > "") { sheet.showRows(i+1)}; to check for changes in the rows and unhide them again?
function onOpen() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const vs = sh.getRange(6, 6, 274, 1).getValues().flat();
vs.forEach((e,i) => {if(!e)sh.hideRows(i + 6);});
}

Hyperlink to range of cells with same name/value - Google Sheets

I have a Spreadsheet with a DASHBOARD + multiple (300+) sheets (within the same spreadsheet). Within the DASHBOARD I have a range column with the names of all sheets.
I'd like to automatically hyperlink all cells to sheet with corresponding name.
Example:
Try this:
function convertToLinks() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("DASHBOARD");
var range = sh.getRange("A11:A15");
var data = range.getValues();
var formulaArr = [];
data.forEach(sheet => {
var targetSheet = ss.getSheetByName(sheet[0]);
if(targetSheet){
var url = ss.getUrl() + '#gid=' + targetSheet.getSheetId();
formulaArr.push(['=HYPERLINK("'+ url +'","'+ sheet[0] +'")'])
}else{
formulaArr.push([sheet[0]]);
}
})
range.setValues(formulaArr);
}
This script above will convert cells A11:A15 to hyperlinks of the corresponding sheets.
Example:
Before:
After:
References:
Range.setValues()
Sheet.getSheetId()
Spreadsheet.getSheetByName()
You do not need hyperlinks to quickly jump to another sheet. Instead, use the name box. It is located in the formula bar to the left of the ƒ𝑥 symbol.
Click the name box and type part of a sheet name to quickly locate that sheet, then press Enter to go to the sheet.
Try this script, but also add search for the cell you need to go to (this can be done by a formula in column C) or filter based on some more info ...
function onEdit(event){
var sh = event.source.getActiveSheet();
var cel = event.source.getActiveRange();
if (sh.getName()=='DASHBOARD' && cel.getColumn()==2 && cel.getRow()>1 && cel.getValue()){
try {
cel.setValue(!cel.getValue())
// add the right destination in the targeted sheet ...
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(cel.offset(0,-1).getValue()).activate()
}
catch(err){
}
}
}

Google Sheets Script - Reference a specific cell in a row

I have a sheet where when I change a specific cell to "YES", I need a template sheet to be copied to a new version and named as per the value of a cell on the current row.
I'm having trouble working out how to get the value of the first cell in the row selected. This is what I have so far (I know this is wrong):
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = sheet.getCurrentCell();
if (currentCell = "YES")
{
SpreadsheetApp.getActiveSpreadsheet().toast("New change control sheet added to workbook.","Change Control",15);
var sourceRow = ss.getActiveRange().getRowIndex();
var tabName = ss.getRange(cell,1).getValues();
ss.getSheetByName("CCTemplate").showSheet()
.activate();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.duplicateActiveSheet();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.getActiveSheet().hideSheet();
ss.setActiveSheet(ss.getSheetByName('Copy of CCTemplate'), true);
ss.getActiveSheet().setName("CC" & tabName);
}
}
Any ideas?
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()=='Your Sheet Name' && e.value=="YES") {
e.source.toast="New change control sheet added to workbook.","Change Control",15);
var tabName=sh.getRange(e.range.rowStart,1).getValue();
var tsh=e.source.getSheetByName('CCTemplate');
var csh=tsh.copyTo(e.source);
csh.setName('CC'+tabName);
}
}
You should avoid using activate in your scripts especially in simple triggers where you have to finish in 30 seconds. I think this code does the same thing that you intended for your code. One significant difference is that I use the information that comes in the event object that comes with the trigger. You should add the code Logger.log(JSON.stringify(e)) and then look at the logs you will see that there is a lot of information available to you which removes the need to run extra functions to get things like a spreadsheet.
Use event objects
onEdit offers among others the event objects range and value which are helpful to retrieve the range that has been edited and its value.
Also
When you want to a cell and compare it against a value, like in if (currentCell = "YES") - you need to retrive its value (either currentCell.getValue() or just event.value) and you need to use == instead of = for comparison.
Be careful with getValues() vs getValue(). The former gives you a 2D array and is not necessary if you want to retrieve the value of a single cell.
There is no need to set your sheet to active in order to change its name.
You can rewrite your code as following:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = event.range;
var value = event.value;
if (value == "YES")
{
...
var sourceRow = range.getRowIndex();
var tabName = ss.getRange(sourceRow, 1).getValue();
...
ss.getSheetByName('Copy of CCTemplate').setName("CC" + tabName);
}
}

Trying to loop setFormula() through open sheets

I'm using an add-on (toTabs) that separates a sheet of data based on the value in column H. It generates a new sheet for each unique value in H2:H, and then places a filter() in cell A2 on each sheet. This works wonderfully, except my next step is to export each sheet to .csv, and I need to get rid of H:H before doing that.
I'm trying to write a script that changes each filter from A2:H to A2:G and either ignores the rest of the formula, or replaces the text that the filter searches for to the sheet name, then loops through every sheet except for specific sheets. I feel like I'm 95% of the way there, but the loop isn't working quite right. Here's what I have so far:
function Loop() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
// Array holding the names of the sheets to exclude from the execution
var exclude = ["Raw Input","Remove Chuff","Filtered Input","Final Input","Data Values","blank","totabs"];
for(var s in allsheets){
var sheet = allsheets[s];
// Stop iteration execution if the condition is meet.
if(exclude.indexOf(sheet.getName())==-1) continue;
var cell = s.getRange("A2");
cell.setFormula('=Filter(\'Data Values\'!A2:G,\'Data Values\'!H2:H="'+ SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName() + '")');
s.getRange('H:H').activate();
s.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
s.getRange('F:F').activate();
s.getActiveRangeList().setNumberFormat('00000');
}
}
Here's a copy of the file with all the irrelevant tabs hidden. Any help would be welcome.
Try this:
function Loop() {
var ss=SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
var exclude = ["Raw Input","Remove Chuff","Filtered Input","Final Input","Data Values","blank","totabs"];
for(var i=0;i<allsheets.length;i++){
var sheet = allsheets[i];
if(exclude.indexOf(sheet.getName())!=-1) continue;
sheet.getRange("A2").setFormula('=Filter(\'Data Values\'!A2:G,\'Data Values\'!H2:H="'+ sheet.getName() + '")');
sheet.getRange(1,8,sheet.getLastRow(),1).clear({contentsOnly: true, skipFilteredRows: true});
sheet.getRange(1,6,sheet.getLastRow(),1).setNumberFormat('00000');
}
}

Delete a row in Google Spreadsheets if color of cell is a certain color

So I've be racking my brain about this one for a while now, not 100% sure why this code isn't working.
I got the code from a previous SO post, from there I changed the script to do what I needed.
function onOpen() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// create menu
var menu = [{name: "Evaluate Column J/Call Duration", functionName: "deleteRow"}];
// add to menu
ss.addMenu("Delete Calls Under 1 Minute", menu);
}
function deleteRow() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get active/selected row
var activeRow = ss.getActiveRange().getRowIndex();
// get content column J
var columnJ = ss.getRange("J"+activeRow).getFontColor();
// evaluate whether cell has white text or not
if (columnJ == 'white' || columnJ == '#FFFFFF') {
ss.deleteRow(parseInt(activeRow));
}
}
Basically I have a spreadsheet that's exported from a super archaic call tracking software. Our main issue is that we need to take out any call that's under 1 minute and any duplicates. So, we added conditional formatting for both. This is for getting rid of any 1 minute calls, which in turn should delete the whole row.
Anyone have any idea about this? Thanks guys.
Column j is 10. Colors are in '#ffffff' format. Red is '#ff0000'. This function will delete the row that has the selected color in the selected column.
function jakeDeleteRow(column,color) {
var column = (typeof(column)!='undefined')?column:10;
var color = (typeof(color)!='undefined')?color:'#ff0000;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sht = ss.getActiveSheet();
var rng = ss.getDataRange();
var rngA = rng.getBackgrounds()
for(var i=rngA.length-1;i>-1;i--)
{
if(rngA[i][column-1]==color)
{
sht.deleteRow(i+1);
}
}
}