Hide/Unhide Columns - google-apps-script

I'm looking for help with a macro that will show a set of columns then another that will show that same set of columns that is assigned to two separate buttons.
Currently I have the Hide Bootcamp and Show Bootcamp buttons that will accomplish this task, but I know that columns will need to be added in to this work sheet that will throw off the ranges in the future (<> Column I OR Columns BJ:CO). Does anybody have a solution that will keep the range integrity as columns are added/removed to the worksheet?
Worksheet Link
https://docs.google.com/spreadsheets/d/17H9QMJ7Lmznon8G0dO4MGkidE0b6ymrvgt7fTE9gQBw/edit?usp=sharing
Current Code Below
Hide Columns
function HideBootcamp() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('I:I').activate();
spreadsheet.getActiveSheet().hideColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('BJ:CO').activate();
spreadsheet.getActiveSheet().hideColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('A2').activate();
};
Show Columns
function ShowBootcamp() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('I:I').activate();
spreadsheet.getActiveSheet().showColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('BJ:CO').activate();
spreadsheet.getActiveSheet().showColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('A2').activate();
};

Hide Column By Name
If you answer yes to both of my questions in the comment above then this will hide your column for you. All you have to do is give the function the name of your column in the top row and of course you will have to change the sheet name probably.
function hideColumnByName(name) {
var name=name||'HDR10';//debug
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Master');
var rg=sh.getRange(1,1,1,sh.getLastColumn())
var hA=rg.getValues()[0];
sh.hideColumns(hA.indexOf(name)+1);
}

It is missing activate range.
function hideColumnByName(name) {
var name=name||'HDR10';//debug
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Master');
var rg=sh.getRange(1,1,1,sh.getLastColumn()).activate();
var hA=rg.getValues()[0];
sh.hideColumns(hA.indexOf(name)+1);
}

Related

Name new sheet according to cell value

Trying to combine two scripts here:
1) Get a cell value in a specific sheet
function getval() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('master'), true);
spreadsheet.getCurrentCell().offset(2, 4).activate();
};
2) Create a new sheet and rename it according to cell value
spreadsheet.insertSheet(1);
spreadsheet.getActiveSheet().setName('');
The second step is where I struggle. How can I use the results of the getval function as a variable to place inside setName?
function insertAndNameNewSheet() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('master');
var name=sh.getCurrentCell().offset(2, 4).getValue();//2 rows down and 4 columns to the right
ss.insertSheet(name,1);
}
insertSheet(name,index)
Best Practices

Adding a row to a named range

I have a named range in Google Sheets (A1:K14). All I'm trying to do is add a new row at the bottom of the named range. It seems like an easy task. The named range doesn't expand using this code and I don't get an error message. It does insert a new row outside of the named range, which is not what I'm trying to do. If I change to insertRowBefore (lastRow), the new blank row is inserted and the named range is expanded. I'm teaching myself GAS, so this is probably a simple mistake on my part
var sheet = SpreadsheetApp.getActiveSheet()
var Range = sheet.setActiveRange(range);
var data = Range.getValues();
Logger.log(range.getA1Notation());
var lastRow = SpreadsheetApp.getActiveSheet().getRange('DataRange').getNumRows();
SpreadsheetApp.getActive().insertRowAfter(lastRow);
return range;
Adding rows and/or columns to a namedRange
This function has the defaults setup to add one row.
//r is number of rows to add
//c is number of columns to add
function addRowAndColumnsToNamedRange(name,r,c) {
var name=name||'One';//default name
var r=r||1;//default number of rows to add
var c=c||0;//default number columns to add
var ss=SpreadsheetApp.getActive();
var nrgA=ss.getNamedRanges();
for(var i=0;i<nrgA.length;i++) {
if(nrgA[i].getName()==name) {
var nr=nrgA[i];
var h=nr.getRange().getHeight();
var w=nr.getRange().getWidth();
var sh=nr.getRange().getSheet();
var row=nr.getRange().getRow();
var col=nr.getRange().getColumn();
var rg=sh.getRange(row,col,h+r,w+c);
ss.setNamedRange(name,rg);
break;
}
}
}
Class NamedRange
Class Range
I think one of the approach can be to get that NamedRange and expand it explicitly.
Something like this:
Get that NamedRange using getNamedRanges() :
Set range of that NamedRange including new row using setRange(range)

Can I protect the name of a Google sheet? Or reference the sheet by position rather than name?

I manage a Google spreadsheet that has multiple sheets. I use scripts to automate some processes on the first sheet. Recently, a user accidentally renamed the first sheet -so the script stopped working until I found out and corrected the name of the sheet.
My question is: how can I protect against this happening again? Is there a way to protect the sheet name from being changed by users other than myself? Or, in the script, is there a different way to reference the first sheet, regardless of its name? I.e., instead of sheet.getSheetName is there something like sheet.getFirst that returns the first sheet, regardless of its name?
Here's a sample of one script that is being affected.
function onOpen(){
//Sheet where this script should run
var SHEETNAME = "Scan IN Check OUT"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if (sheet.getSheetName() == SHEETNAME ) { //Check we are on the correct sheet
var sheet = SpreadsheetApp.getActiveSheet();
//Find the last cell with data in that specific column (A in this case)
var lastCell = sheet.getRange('A1').getNextDataCell(SpreadsheetApp.Direction.DOWN);
//Activate the next cell
lastCell.offset(1, 0).activate();
}
}
SpreadsheetApp.getActive().getSheets()[0]; will always be the first sheet on the left which may not always be the same sheet since users can change their position. You can use the first function below to see how the indexes change as you move sheets around. WARNING: don't do this in a spreadsheet that contains important information because it first clears the sheet and then writes the name, the id's and the indexes for all sheets.
So you can also use the first function to get the id of the sheet that you want not to change.
The second function will get the sheet by id. If you will select the id of your sheet and replace the string 'My Sheet ID' with it and also put the correct name of your sheet where it says my sheet name, then when you runTwo() it will move you sheets all the way to left (ie index 1) and rename to whatever you want.
function runOne() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var sht=ss.getSheets();
sh.clear();
sh.appendRow(['Name','id','index']);
sht.forEach(function(s){
sh.appendRow([s.getName(),s.getSheetId(),s.getIndex()]);
});
}
function getSheetById(id) {
var id=id||'My Sheet ID';
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
for(var i=0;i<shts.length;i++) {
if(shts[i].getSheetId()==id) {
return shts[i];
}
}
}
function runTwo() {
var ss=SpreadsheetApp.getActive();
var sh=getSheetById().activate();
ss.moveActiveSheet(1);
sh.setName('My Sheet Name');
}
Of course the real problem here is that if the user can change the name of your sheet they can also go in and change your script.

Google Sheets appendRow with formula values

My goal is to write a script in Google Sheets that will copy two (or more) values from specific cells on a single row on a worksheet to the bottom of the list on another. I need to copy the actual values as well as calculate additional from the original values.
This is my first attempt and should explain what I am trying to do:
function CopyCals() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Sheet2'), true);
spreadsheet.appendRow(['=today()','','=Sheet1!B30','=Sheet1!E30','=Sheet1!B30-Sheet1!E30','=Sheet1!B30-(Sheet1!E30-2500)']);
};
This works great, except it appends the actual formulas, rather than the values of the fields. As the fields change, so do the values of what was copied.
To work around this, I tried getRange but I can't figure out how to select specific elements (i.e. the values of C30 and E30) in the array. Below is what I tried:
function copyCals2() {
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange("B30:E30").getValues();
spreadsheet.appendRow(range[0]);
};
How do I get appendRow to only print the values for C30 and E30 and use them in formulas as shown in my original attempt.
Getting the desired data
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
};
This is probably easier to write:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var b30=sh1.getRange('B30').getValue();
var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
I would probably prefer doing it something like this:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getRange('B30:B31').getValues();
var b30=vA[0][0];
var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
If you had a lot of data and it's all grouped together nicely then the later can save you lot's of time because you only have to do one read to get all of the data at one time.
In the latter approach its helpful to use your debugger to help you to get a picture of what the data looks like:
vA=[[value in B30],[value in B31]] so the vA[0][0] is B30 and vA[1][0] is B31
And here is yet another way to write the getRange() function:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getRange(30,2,2,1).getValues();
//var vA=sh1.getRange('B30:B31').getValues();
var b30=vA[0][0];
var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
And just to make matters even a little more complicated for you a lot of times it's helpful to get the entire page of values all at one time so in that case this is what you left with.
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getDataRange().getValues();
var b30=vA[29][1];
var b31=vA[30][1];
//var vA=sh1.getRange('B30:B31').getValues();//gets all the data at one time
//var b30=vA[0][0];
//var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
All of these function do the same thing you can pick which one you want depending upon what your trying to accomplish.
Is there a way to do this if the row you are appending is not known?
For example I want to append a row with mixed data and a formula as such:
var values = [today, record['Date'], record['EventType'], record['Display Name'],
'=A?'];
var ss = SpreadsheetApp.openById(sponsorSheetId);
var sheet = ss.getSheetByName('SentLog');
sheet.appendRow(values);
Where, in A?, ? is the last row. But since I haven't appended yet, I wouldn't know what the last row is. And this needs to be a formula that refers to cell in column A.

Hide or filter rows or columns if specific sheets are empty in a google sheet macro

I have been trying to conditionally hide or filter a column if a range is empty. I guess the function "if" does not work since the column is hidden even when the range is not empty.
I will thank if someone check my code.
function OcultarSi() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheets()[0];
var range = sh.getRange('E2:E24');
if (range.isBlank()){
spreadsheet.getRange('E:E').activate();
spreadsheet.getActiveSheet().hideColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
}
};
Below the code working for any user who needs a similar one. . .
function OcultarSi(){
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange('E2:E24');
if (range.isBlank()){
spreadsheet.getRange('E:E').activate();
spreadsheet.getActiveSheet().hideColumns(spreadsheet.getActiveRange().getColumn(),spreadsheet.getActiveRange().getNumColumns());
}
};