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

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){
}
}
}

Related

Have only the rows containing the value in Column B copied into the new sheet

I am trying to accomplish 2 things with Google Sheets. I have a Main sheet with data and I would like to:
Create new sheet with the name based on a cell value in Column B (accomplished)
Copy the rows containing those values in B in that new sheet
Part 1 works fine with this script:
function onOpen() {
var menu = [{
name : "Add",
functionName : "newSheet"
}
];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Sheet", menu);
}
function newSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var templateSheet = ss.getActiveSheet();
var sheet1 = ss.getSheetByName("Sheet1")
var getNames = sheet1.getRange("B2:B").getValues().filter(String).toString().split(",");
for (var i = 0; i < getNames.length; i++) {
var copy = ss.getSheetByName(getNames[i]);
if (copy) {
Logger.log("Sheet already exists");
} else {
templateSheet.copyTo(ss).setName(getNames[i]);
ss.setActiveSheet(ss.getSheetByName(getNames[i]));
ss.moveActiveSheet(ss.getNumSheets());
}
}
}
The problem is when new sheets are created it copies the content of the main sheet. I would like to have only the rows containing the value in Column B copied into the new sheet.
Instead of using copyTo you might use one of the insertSheet methods of Class Spreadsheet, then copy rows having the required value in column B into the new sheet.
The specific code to copy the rows depends on what really need to copy (values, displayed values, formulas, cells formatting, rich text cell content formatting, notes, data validation, conditional formatting)
Let say that you are only interested in passing the values, you could use something like the following in the getNames for loop:
var data = sheet1.getDataRange().getValues().filter(row => row[1] === getNames[i])
var newSheet = ss.insertSheet(getNames[i]);
if( data.length > 0 ) newSheet.getRange(1,1,data.length,data[0].length).setValues(data);
Related
insertSheet advanced options
check for existence of a sheet in google spreadsheets
You can just add one more function to remove redundant rows from any sheet. Something like this:
function main() {
var sheet = SpreadsheetApp.getActiveSheet()
remove_rows("aaa", sheet); // remove all rows, that contain 'aaa' in first column
}
function remove_rows(value, sheet) {
var data = sheet.getDataRange().getValues();
var new_data = data.filter(x => x[0].indexOf(value)>=0)
sheet.getDataRange().clearContent();
sheet.getRange(1,1,new_data.length,new_data[0].length).setValues(new_data);
}

Is there a way to add record counts to google sheet TAB name

I like to append record counts for each sheet to the tab name in google sheet.
The google sheet has a few tabs. (i.e Sales, Inventory, etc.)
I like the tab names to dynamically show the record counts in the tab name
Example: Tab names (Sales-10, Inventory-100)
I have searched and can't find a reference to how this could be done.
Thanks
Try below script:
Assuming, you have count values in A1 in both sheets, if not then change your range accordingly in getRange(1,1)
function appendName() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (var i=0;i<sheets.length;i++){
var oldName = sheets[i].getName();
var getcountName = sheets[i].getRange(1,1).getValue();
var re = /\W\d+/ ;
var oldNameReset = oldName.replace(re , '') ;
var setNewName = sheets[i].setName(oldNameReset + "-" + getcountName);
}
}
Set trigger On Change
Assume counts in A1 or anywhere you want. Don't use dashes in sheets that you don't want counts in.
function addCounts() {
const ss=SpreadsheetApp.getActive();
const shts=ss.getSheets();
shts.forEach(function(sh,i){
let idx=sh.getName().lastIndexOf('-');
if(idx!=-1) {
sh.setName(sh.getName().slice(0,idx+1) + String(sh.getRange('A1').getValue()));
}
});
}

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);
}
}

How to create hyperlink to range in Google Sheets App Script?

I'm trying to fill cells with hyperlinks to ranges in Google Sheets app script with the same desired outcome I would get had I done it in GUI. I managed to create hyperlinks to sheet in the form of "gid=..." with the ... being a sheetID. But I struggle to get the rangeID that is used when generating the hyperlink in GUI e.g.
HYPERLINK("#rangeid=1420762593";"'List 4'!F2:F15")
Is it possible to create hyperlinks to ranges in app script?
Yes, you can do this in App Script. Here's a very simple implementation where the HYPERLINK function is built and appended to a cell:
function hyperlinkRange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet1");
var sheet2 = ss.getSheetByName("Sheet2").getSheetId();
sheet1.getRange("A1").setValue('=hyperlink("#gid='+sheet2+'&range='+sheet1.getRange('A1:A10').getA1Notation()+'", "Click to jump to Sheet 2")');
}
You can combine this with loops to set a value of links across multiple sheets.
Custom functions
Use in a formula.
Simple range:
=HYPERLINK(getLinkByRange("Sheet1","A1"), "Link to A1")
Named range:
=HYPERLINK(getLinkByNamedRange("NamedRange"), "Link to named range")
The code, insert into the script editor (Tools > Script Editor):
function getLinkByRange(sheetName, rangeA1, fileId)
{
// file + sheet
var file = getDafaultFile_(fileId);
var sheet = file.getSheetByName(sheetName);
return getCombinedLink_(rangeA1, sheet.getSheetId(), fileId, file)
}
function getLinkByNamedRange(name, fileId)
{
// file + range + sheet
var file = getDafaultFile_(fileId);
var range = file.getRangeByName(name);
var sheet = range.getSheet();
return getCombinedLink_(range.getA1Notation(), sheet.getSheetId(), fileId, file)
}
function getDafaultFile_(fileId)
{
// get file
var file;
if (fileId) { file = SpreadsheetApp.openById(fileId); }
else file = SpreadsheetApp.getActive();
return file;
}
function getCombinedLink_(rangeA1, sheetId, fileId, file)
{
var externalPart = '';
if (fileId) { externalPart = file.getUrl(); }
return externalPart + '#gid=' + sheetId + 'range=' + rangeA1;
}
Here is another example. Hopefully, it is clean and self-explanatory
function hyperlinkRange(shDest,rgDest,shSrc,rgSrc,linkText) {
// get the spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet()
// get the destination sheet id
var idDest = shDest.getSheetId()
// link the range
var formula = '=hyperlink("#gid='+idDest+'&range='+rgDest+'","'+linkText+'")'
shSrc.getRange(rgSrc).setValue(formula)
}
In case you want to create a link to another sheet which will open the sheet in the same browser tab here is what you want to do:
1. Get the id of the sheet. Check the link in your browser and you will see #gid=x where x is the sheet id
2. Then you want to set the formula (hyperlink) to the cell and make it show as a hyperlink
SpreadsheetApp.getActiveSheet().getRange("A1").setFormula('=HYPERLINK("#gid=X","test")').setShowHyperlink(true);
If you don't use setShowHyperlink(true) it will be shown as a regular text.

Go to last line of data in Google Sheets

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.