set newly created google sheet as the active sheet - google-apps-script

I am using google sheets to import data into a mysql database. Each month a new sheet gets created for instance 06_2017. Is there a way to set the newly created sheet as active sheet or based on current month & year set that sheet as active. Instead of having to hard code
var gsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('06_2017');
var gsheetname = sheet.getSheetName();
var gdata = sheet.getDataRange().getValues();

As #Cooper have said, you can store the name of the newly created sheet in UserProperties, then place your code in onOpen() method of your script.
You can do it like this,
function onOpen(e)
{
var prop = PropertiesService.getDocumentProperties();
var sheet_name = prop.getProperty(<NAME_OF_SHEET>);
var sp = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet_name).activate();
}

If you are going to stick to the pattern 'MM_YYYY' for naming your sheets, the following code will always activate the sheet with the latest date. If your plan is to use different naming patterns in a single document, you need to refactor the code below to allow for this.
The onOpen() function is one of the so-called simple triggers in GAS, so every code inside this function will be executed when you open the document. The sort() method will sort array elements based on given criteria. Since sort() doesn't know what criteria you'd like to use for sorting Sheet objects, you need to pass a comparator function to override the default one.
The comparator function parses sheet names and creates date objects by passing year and date (in that order) to the new Date() constructor. It then uses the result of date subtraction to sort the 'sheets' array in descending order, from the highest to the lowest value.
Finally, you take the first element of the sorted array (that would be the latest created sheet) and set it as the active sheet.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
sheets.sort(function(a,b) {
var arr1 = a.getName().split("_");
var arr2 = b.getName().split("_");
return new Date(arr2[1], arr2[0]) - new Date(arr1[1], arr1[0]);
});
if (sheets.length < 1) {
return;
}
ss.setActiveSheet(sheets[0]);
}

Related

Get a non-contiguous active range by selection and use it programmatically Google script

I want to get a non-contiguous range by selection and use it programmatically.
If I select say, A1 to F10 so I have a Contiguous range I can use below and it will give A1:F10
function getSelectedRange_Contiguous(){
var selected = SpreadsheetApp.getActiveSheet().getActiveRange(); // Gets the selected range
var rangeString = selected.getA1Notation(); // converts it to the A1 type notation
Logger.log(rangeString)
return rangeString;
}
If I use the macro recorder and select A1:A10 & C1:C10 & E1:E10 I get a function that will select a discontinuous range
function UntitledMacro() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRangeList(['A1:A10', 'C1:C10', 'E1:E10']).activate();
};
But how do you get a Non-Contiguous range from selection to be used programmatically
Non-Contiguous does not work, it gives E1:E10
function getSelectedRange_NonContiguous() {
var spreadsheet = SpreadsheetApp.getActive();
var selected = spreadsheet.getActiveRange().getRangeList([]);
var rangeString = selected.getA1Notation();
Logger.log(rangeString)
return rangeString;
};
Explanation:
You need to use getActiveRangeList() instead of getActiveRange to get the Non-Contiguous active range.
Also it is better if you apply this method to a particular sheet instead of the spreadsheet object so you can later on be more specific on the sheet object.
Solution:
function getSelectedRange_NonContiguous() {
const sheet = SpreadsheetApp.getActive().getActiveSheet();
const selected = sheet.getActiveRangeList().getRanges();
// an array of selected ranges
const notations = selected.map(rng=>rng.getA1Notation());
// log them:
notations.forEach(nt=>console.log(nt));
return notations;
};
Keep in mind that if you are planning to use that function as a custom function (a custom formula in your sheet) you won't get live updates because of the custom formula restrictions.

Script to cause an ImportRange function to run on demand

first, let me say, i am not a programmer, but i understand and can build spreadsheet ok. I'm newer to doing so in Google Sheets. I am using an Importrange function to copy date from one spreadsheet tab to another spreadsheet. I'm finding that there is a delay in the updating. I checked my Settings for recalculations and Google Sheets is set to refresh when data is changed. However, its not doing so. So, i wanted to create a button the user can hit and force the ImportRange refresh.
I found some code that appears to accomplish this, but I can't get it to work:
function myFunction() {
SpreadsheetApp.getActive().getRange('A1').setValue('IMPORTRANGE('https://docs.google.com/spreadsheets/d/abcd123abcd123', 'sheet1!A:B')')
}
Here is my import range which is in CELL C4 of a sheet named CALCS:
=importrange("https://docs.google.com/spreadsheets/d/1B5PEI5f4-TAhS77-poCYTiWI6w2t2BEzfiP5kXVq9lk/edit#gid=1661941505","PO TRACKING!A4:T1000")
Welcome to the wonderful world of scripts! There is so much you can do with your projects when using Google Apps Script.
Your question is a good one but I took a different approach. Rather than using an import range that needs to be recalculated, I just wrote a script that would do a similar thing. If you need to collect a specific range of data instead of the entire sheet, then just make one small change to the script provided.
//CHANGE
var poData = poTab.getDataRange().getValues();
//TO
var poData = poTab.getRange('INSERT A1 NOTATION HERE').getValues();
That should allow you to select a specifc range if needed.
//TRANSFER DATA FROM ONE TAB TO ANOTHER
function transferFromSameSpreadsheet() {
//GETS ACTIVE SPREADSHEET
var ss = SpreadsheetApp.getActiveSpreadsheet();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS STORED
var poTab = ss.getSheetByName('Po Tracking');
//STORES ALL DATA FROM SHEET
var poData = poTab.getDataRange().getValues();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS SENT
var transferTo = ss.getSheetByName('insert sheet name here');
//CLEARS OLD DATA
transferTo.clearContents();
//UPDATES SHEET WITH NEW DATA
transferTo.getRange(1,1,poData.length,poData[0].length).setValues(poData);
}
//TRANSFER DATA FROM A MASTER SHEET TO ANOTHER WORKBOOK. RUN SCRIPT IN ACTIVE WORKBOOK, NOT MASTER.
function transferFromDifferentSpreadsheet() {
//OPENS SPREADSHEET BY ID
var ms = SpreadsheetApp.openById('insert spreadsheet id here');
//GETS ACTIVE SPREADSHEET
var ss = SpreadsheetApp.getActiveSpreadsheet();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS STORED
var poTab = ms.getSheetByName('Po Tracking');
//STORES ALL DATA FROM SHEET
var poData = poTab.getDataRange().getValues();
//GETS SPECIFIC SHEET BY NAME WHERE DATA IS SENT
var transferTo = ss.getSheetByName('insert sheet name here');
//CLEARS OLD DATA
transferTo.clearContents();
//UPDATES SHEET WITH NEW DATA
transferTo.getRange(1,1,poData.length,poData[0].length).setValues(poData);
}
Good luck!
Get range A1 notation documentation: https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrangea1notation
What your code is doing is just setting the cell value as the string you input in the setValue()
If you want to input formula you can either include "=" when using setValue() or use setFormula() method
Sample:
function myFunction() {
SpreadsheetApp.getActive().getRange('A1').setFormula('IMPORTRANGE("https://docs.google.com/spreadsheets/d/xxxxx", "sheet1!A:B")');
SpreadsheetApp.getActive().getRange('D1').setValue('=IMPORTRANGE("https://docs.google.com/spreadsheets/d/xxxxx", "sheet1!A:B")');
}
Note:
I also fixed the string format in your code so that it will provide a string input to the setValue() and setFormula()
References:
Range.setValue()
Range.setFormula()

How can I avoid a VLOOKUP Error that occurs when I delete a name?

The pictures used are only from an example sheet! My basic problem is that I have a list called Assignment in which names appear (dropdown list). For Location (in the assignment sheet) I use the following formula: =IF(C2<>"",VLOOKUP(C2,'Input Data'!C$3:D$7,2,FALSE),"")
These names are assigned certain values, they are in the same line. The names are defined in a worksheet called Input Data!
If I now delete a name like Green, John from the Input Data worksheet, then I get the following error in another worksheet (Evaluation). (More than 40 people have access to this worksheet and randomly delete names)In this evaluation worksheet the values are evaluated by the following formula:
=ARRAY_CONSTRAIN(ARRAYFORMULA(SUM(IF((IF($B$2="dontcare",1,REGEXMATCH(Assignment!$E$3:$E$577,$B$2 &"*")))*(IF($B$3="dontcare",1,(Assignment!$E$3:$E$577=$B$3)))*(IF($B$4="dontcare",1,(Assignment!$D$3:$D$577=$B$4)))*(IF($B$5="dontcare",1,(Assignment!$F$3:$F$577=$B$5)))*(IF($B$6="dontcare",1,(Assignment!$B$3:$B$577=$B$6))),(Assignment!S$3:S$577)))), 1, 1)
The following error appears in the evaluation sheet:
Error:
During the evaluation of VLOOKUP the value "Green, John" was not found.
How can I avoid this error? Is it possible to avoid this error with a macro that deletes Names from assignment sheet that are not in the Input data sheet? Do you have any ideas for a code?Maybe a Formula or perhaps a Macro?
example sheet with explanation: https://docs.google.com/spreadsheets/d/1OU_95Lhf6p0ju2TLlz8xmTegHpzTYu4DW0_X57mObBc/edit#gid=1763280488
If what you want to do is make sure that rows are deleted in a sheet when there are incorrect values you could try something like this in Apps Script:
function onEdit(e) {
var spreadsheet = e.source;
var assignment = spreadsheet.getSheetByName("Assignment");
var assignmentRange = assignment.getDataRange();
var assignmentNames = assignment.getRange(3, 2, assignmentRange.getNumRows());
var inputData = spreadsheet.getSheetByName("Input Data");
var inputDataRange = inputData.getDataRange();
var i = 1;
while(assignmentNames.getNumRows() > i){
var currentCell = assignmentNames.getCell(i, 1);
var txtFinder = inputDataRange.createTextFinder(currentCell.getValue());
txtFinder.matchEntireCell(true);
if(!txtFinder.findNext()){
assignment.deleteRow(currentCell.getRow())
}else{
// We are only steping when no elements have been deleted
// Otherwise we would skip rows due to shifting in row deletion
i++;
}
}
}
Explanation
onEdit is a special function name in Apps Script that would execute every time it's parent sheet is modified.
After that we retrieve the spreadsheet from the event object
var spreadsheet = e.source;
Now we get the relevant range in the Assignment sheet. Look at the usage of getDataRange documentation to avoid retrieving unnecessary cell values. And from that range we actually get the specific column we are interested on.
var assignment = spreadsheet.getSheetByName("Assignment");
var assignmentRange = assignment.getDataRange();
var assignmentNames = assignment.getRange(3, 2, assignmentRange.getNumRows());
Now we do the same for the other sheet(Input Data):
var inputData = spreadsheet.getSheetByName("Input Data");
var inputDataRange = inputData.getDataRange();
Note: Here I'm not getting a specified column because I assume that the full name will not repeat in any other column. But if you want you could get the specified range as I have done at Assignment.
After that we want to look for specific values in the Assignment range that don't exist in the Input Data sheet, you should try the TextFinder.
For every name in Assignment you should create a TextFinder. I have also forced to make a whole cell match.
var i = 1;
while(assignmentNames.getNumRows() > i){
var currentCell = assignmentNames.getCell(i, 1);
var txtFinder = inputDataRange.createTextFinder(currentCell.getValue());
txtFinder.matchEntireCell(true);
If txtFinder finds a value the findNext() will evaluate to true. In the other hand when the txtFinder does not find a value it will be null and evaluated to false.
if(!txtFinder.findNext()){
assignment.deleteRow(currentCell.getRow())
}else{
// We are only stepping forward when no elements have been deleted
// Otherwise we would skip rows due to shifting in row deletion
i++;
}
}
}

how to get deleteSheet to work for complex sheet names

I have a Google Add-on that manages a few sheets in a Google spreadsheet. One of the things it does is remove sheets based on dates, which are the names of the sheets it deletes. In my current development phase I'm also adding the capability to remove sheets that include the date and another term, specifically "script 6/5/2019", for example.
I'm using the same code that worked for the sheets named with the date and made some adjustments to it, but it returns an error when it comes time to delete the sheet: "Cannot find method deleteSheet(string)"
if(sRemove==true) {
SpreadsheetApp.getActiveSpreadsheet().toast('Finding Old Scripts', 'Status',3);
for (var i = ss.getNumSheets()-1; i >= 0; i--) {
var thisTab = ss.getSheets()[i].getName();
if(thisTab.substr(0,6) =="script") {
Logger.log(i+" "+thisTab+" is a script");
var tabDate = new Date(thisTab.substr(8));
//8 is the first digit of the date
Logger.log(tabDate);
var thisDate = new Date(todayDate);
var patt = new RegExp("s");
var res = patt.test(thisTab);
Logger.log(i+" "+res);
if(tabDate<thisDate && res==true) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//ss.setActiveSheet(ss.getSheetByName(thisTab));
//ss.deleteActiveSheet(ss.getSheetByName(thisTab));
Logger.log(thisTab);
ss.deleteSheet(thisTab);
tabsGone++;
}
}
}
ui.alert(tabsGone+" Sheets Removed");
}
Logger.log returns the correct name of the sheet to be removed, but deleting the sheet returns the error "Cannot find method deleteSheet(string)"
Requirement:
Delete sheet based on name.
Solution:
Use getSheetByName() to get the sheet object then pass this to your deleteSheet():
ss.deleteSheet(ss.getSheetByName(thisTab));
Explanation:
Currently you're passing string thisTab to deleteSheet(), this won't work because it is expecting a sheet object, not a string. As long as your code above is working properly and thisTab matches your sheet name exactly, all you need to do is call getSheetByName(thisTab) to get the sheet object then pass this to deleteSheet().
References:
getSheetByName(string)
deleteSheet(sheet)

Random string generator randomizing too often

I'm looking for input on a small random string generator project.
I'm trying to create a random string generator that puts three words together, where I can then save strings that I like to another sheet.
I'm using a basic script to assign a RANDBETWEEN formula to 3 specific cells, which works great to generate the strings, but it keeps recalculating with each additional step in the script it seems. I'd like to run one function then if I like the string I would run another function to store the string. However, when I try that it has already recalculated the RANDBETWEEN formulas and it saves something completely different.
I have an example sheet here:
https://docs.google.com/spreadsheets/d/1TWziyjjLQJJApkHCqrLzNGMFU0sf-vNEOOSatuhHURo/edit?usp=sharing
And here is the code I'm using for the "Go" and "Save" buttons, respectively:
function generateString() {
var ss = SpreadsheetApp.openById('1b9rP39sgZDOZqu7AmZhOxX9J8CMukmUw7NPY3Qzuq78');
var sheet = ss.getSheetByName('Randomizer');
var cell = sheet.getRange('D4');
var cell2 = sheet.getRange('E4');
var cell3 = sheet.getRange('F4');
cell.setValue('=INDEX(A:A,RANDBETWEEN(1,counta(A:A)))');
cell2.setValue('=INDEX(A:A,RANDBETWEEN(1,counta(A:A)))');
cell3.setValue('=INDEX(A:A,RANDBETWEEN(1,counta(A:A)))');
var cell4 = sheet.getRange('P4');
cell4.copyTo (sheet.getRange ('P5'), {contentsOnly: true}); //an attempt to paste values to record the random string
}
function saveString() {
var ss = SpreadsheetApp.openById('1b9rP39sgZDOZqu7AmZhOxX9J8CMukmUw7NPY3Qzuq78');
var sheet = ss.getSheetByName('Randomizer'); //replace with source Sheet tab name
var range = sheet.getRange('P4'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('1b9rP39sgZDOZqu7AmZhOxX9J8CMukmUw7NPY3Qzuq78');
var tsheet = tss.getSheetByName('Saved Strings'); //replace with destination Sheet tab name
tsheet.getRange(tsheet.getLastRow()+1, 1, 1, 1).setValues(data);
}
Please let me know if anyone has some ideas on how to make this work properly. Thanks!
RANDBETWEEN the same as RAND are volatile functions. That means that their results changes every time that spreadsheet is recalculated.
If you need to keep the randomized to result be "freezed" for a while instead of this functions one alternative to consider is the use of a custom function as they are recalculated only when at least one of its arguments changes.
Related
Refresh data retrieved by a custom function in Google Sheet