how do I reset the google sheets counter? - google-apps-script

Current function: Using apps script, I delete a few tabs then add a few tabs. So new a new sheet is like 'Sheet 467'. The number is getting big very quickly even though there are only 8 sheets in the file.
Desired function: I'd like to reset the sheet counter so the next sheet is 9 (number of sheets currently in the file +1) rather than 468 (number of sheets there have ever been in the file +1).
I tried to google this but I think the search terms are too generic. I couldn't find it anywhere.

Explanation
Unfortunately, this is the default behaviour of how sheets are created. You can request it as a feature but I don't think this will ever be implemented.
However, I would like to propose the following two workarounds:
User inserts a new sheet:
Take advantage of the onChange trigger and especially the event
object property changeType. When you add a new sheet, the latter
takes the value of INSERT_GRID.
This allows you to trigger a piece
of code when a new sheet is added by the user.
The following script will check whether you created a new sheet. If you did, it will rename the last created sheet as Sheet plus the total number of sheets.
Script inserts a new sheet:
If the new sheets are created by a script the onChange trigger can't be used. Then use the second solution I propose.
Workarounds:
Solution when the user inserts a new sheet:
function nameNewSheet(e) {
if (e.changeType == 'INSERT_GRID') {
const sheets = SpreadsheetApp.getActive().getSheets();
sheets[sheets.length-1].setName(`Sheet${sheets.length}`)
}
}
In order to use this solution you have to create an installable onChange trigger for the nameNewSheet function. Please read this answer on how to do that.
Solution when the script inserts a new sheet:
If the script itself adds new sheets, then onChange won't be triggered. But you can incorporate the following two lines into your existing script that inserts new sheets in order to rename the last created sheet.
function myFunction() {
//
// the code of the script that adds a new sheet
//
const sheets = SpreadsheetApp.getActive().getSheets();
sheets[sheets.length-1].setName(`Sheet${sheets.length}`);
}
Illustration:

Related

How to create a function that triggers whenever a new sheet is created? [duplicate]

This question already has an answer here:
How to set sheet create event as a trigger in apps script
(1 answer)
Closed 1 year ago.
I have a spreadsheet with a sheet of modification times for each of my other sheets. For example, I have a 'Signing' and 'Profile' sheet, and in my modifications times sheet I have:
Sheet Name
Modification Time
Signing
1639335205000
Profile
1639335207338
I want to create a function that, whenever I create another sheet, automatically adds it to the modifications times sheet as a new row.
I have looked at ScriptApp triggers and events but haven't found anything that is related(onEdit for example might be useful if there was a way to know if the edit was creating a sheet (if it even catches those events) but would also be triggered all the time).
I've actually wondered this myself and I hope someone can post a better answer than my method.
First, I create a named range that's going to store the number of sheets in the workbook (in below code I used sheetCount). Make sure this is at the workbook level (which by default google does).
Then leverage onEdit with this:
function onEdit(e) {
//var range = e.range;
const ss = SpreadsheetApp.getActiveSpreadsheet();
const storedSheetCount = ss.getRange("sheetCount")//<-- need to setup named range
var sCount = ss.getNumSheets();
if(storedSheetCount.getValue()!=sCount){
if(storedSheetCount.getValue()<sCount){
// more
Browser.msgBox("You addeded a spreadsheet!")
}else{
//for less
Browser.msgBox("you took one away")
}
//both cases update the value
storedSheetCount.setValue(sCount); //<--- updates stored count
}
}
I am well aware that this is not ideal for a variety of reasons including:
No code is executed until an edit ACTUALLY happens.
Stated differently, adding a sheet is not an edit event, so a user must then click into a cell or delete a blank one to kickoff the procedure.
Takes up space on the spreadsheet front end.
I hate helper columns/cells. Names is one area that Excel definitely crushes GoogleShhets as it allows direct references to values. Thus with Excel, I could avoid cluttering a spreadsheet by setting a named rage to the sheetCount (ie. refersTo:=3). One alternative to this would be to use the spreadsheet's file description, but this requires granting permissions to Drive Service which opens up all kinds of security risks for such a trivial request.
If anyone can do better, please share.

Update google "master" spreadsheet when other sheets are updated

I have multiple Google spreadsheets with booking data such as booking number, client name, email, booking date etc. The order of these columns is not the same in all sheets.
I would like to update all data from all my "source" sheets in one "master" spreadsheet. Meaning as soon as a new row is added or an existing row is updated, the data will be synced to the master spreadsheet
What would be the best way to achieve that? Javascript or is there some existing Google Sheets addon?
Example sheet 1: Fast boat bookings
Example sheet 2: Airport transfer bookings
Master sheet
Thanks so much to everyone looking into this!
Most people recommend to use "importrange", but I don't think this works for my use case.
I am also aware that it could be achieved by Zapier, but it would become to costly to pay for so many zaps. I believe there is another solution.
I do not have any code yet to start with :-/
I expect the data in the master sheet to be sorted by submission date and time like this:
The general procedure for using formulas is
Use IMPORTRANGE to get the data from the source spreadsheets into the master spreadsheet
Use array notation to put the imported data together
NOTES:
If you are new to using IMPORTRANGE, arrays in Google Sheets and complex formulas, use one sheet for each IMPORTRANGE, and delete the unused columns to save cells because the Google Sheets 5 million cell limit
If you prefer to use scripts, you should get the spreadsheets keys, or URLs then you could use SpreadsheetApp.openById(id) or SpreadsheetApp.openByUrl(url) to open the spreadsheets. Then you could use getValues() / setValues() to read / write the values from source spreadsheets to the master spreadsheet.
References
Using arrays in Google Sheets
Extending Google Sheets with Google Apps Script
Related
Is it possible to do ImportRange in Google Apps Script?
IMPORTRANGE to import multiple Google Sheets into one vertical column?
As a workaround for Apps Script triggers not being fired by automatic sheet updates, you need to use IMPORTRANGE to import your data into a dummy sheet. IMPORTRANGE will detect also through automatic sheet update and simultaneously it is able to fire an onEdit trigger.
Do the following:
Delete all empty rows in MASTER spreadsheet
Create a dummy spreadsheet and import into it the contents of MASTER spreadsheetwith a formula:
From the dummy sheet, open the script editor and insert the following code:
function changed(e) {
var masterSheet = SpreadsheetApp.getActive().getSheetByName("Sheet1");
if(PropertiesService.getScriptProperties().getKeys().length==0){
PropertiesService.getScriptProperties().setProperty('startRow', 5);
}
var startRow=PropertiesService.getScriptProperties().getProperty('startRow');
var lastRow=masterSheet.getLastRow();
var numRows=lastRow-startRow+1;
var startCol=1;
var numCols=6;
var values=masterSheet.getRange(startRow,startCol,numRows,numCols).getValues();
var FBsheet=SpreadsheetApp.openById('1SzBx5Q9rrlcLGSqD8y5eFE5TX304LfZ7D9mxxrHhfKw').getSheetByName('Sheet1');
var ATsheet=SpreadsheetApp.openById('1IRD8wT5Kmx7h_xP807f4ibWRn8g98RjA-dJXxADXAl0').getSheetByName('Sheet1');
FBsheet.getRange(FBsheet.getLastRow()+1,startCol, numRows, numCols).setValues(values);
var ATlastRow=ATsheet.getLastRow();
for(var i=0;i<numRows;i++){
Logger.log(values[i][1]);
ATsheet.getRange(ATlastRow+1+i,1, 1, 1).setValue(values[i][1]);
ATsheet.getRange(ATlastRow+1+i,2, 1, 1).setValue(values[i][0]);
ATsheet.getRange(ATlastRow+1+i,3, 1, 1).setValue(values[i][3]);
ATsheet.getRange(ATlastRow+1+i,4, 1, 1).setValue(values[i][2]);
ATsheet.getRange(ATlastRow+1+i,5, 1, 1).setValue(values[i][5]);
ATsheet.getRange(ATlastRow+1+i,6, 1, 1).setValue(values[i][4]);
}
PropertiesService.getScriptProperties().setProperty('startRow', lastRow+1);
}
Attach to the script an onEdit trigger
Run the script once manually, it is normal that it will throw you an error
Now the script will run automatically each time data update takes place in MASTER spreadsheet.
Make sure you change all spreadsheetIDs and sheet names with your
values.

CopyTo: How to push data/copy a cell from one google spreadsheet to another google spreadsheet using google apps script?

I look for a solution to copy a specific cell value in the Source spreadsheet from tab "Sum all" to another Spreadsheet to the Target Spreadsheet Tab "Copy all". It should work every time i change the Value of Cell G10. Access to the Target sheet is granted before i enter any Value to G10.
(Source Tab Name is "Sum all:G10" - Sheet has 10 different Tabs)
(Target Tab Name is "CopyData:T12" - Sheet has 10 different Tabs)
Easy way
Use the built-in IMPORTRANGE() function in Google Apps:
In your Target cell, type the following formula
=IMPORTRANGE("FILE_ID_HERE","Sum all!G10:G10")
The syntax for this function is
=IMPORTRANGE("FILE_ID","SHEET_NAME!RANGE_START:RANGE_END")
When you first type in this function, you'll get an error in the cell. Simply click on it and select "Allow" to link the two sheets together. This error will occur even if it is the same spreadsheet. This function can link two separate spreadsheets, too, as long as you have edit access to both.
Hard Way
I'm assuming from your question that you want to copy values to and from the same spreadsheet document, but to different cells that are located on different sheets of the spreadsheet. The Google Apps Script API calls tabs "sheets" and the overall document "spreadsheet".
First, open the script editor
Open your spreadsheet that you'd like to make this script for.
Select "Tools" in the toolbar, then "Script Editor"
Second, make a function for onEdit.
Making a function named onEdit will create a function that runs every time the edit trigger is fired, using a no-authorization "simple trigger". Google Sheets automatically sends this event every time a cell is edited by a user. The argument e for the function is the event passed by the trigger.
function onEdit(e) {
// Get the sheet named "Sum all" from the active spreadsheet (i.e. the one you are editing)
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sum all");
// Returns the active cell
var cell = source .getActiveCell();
// Compare to see if its the right cell you're looking for
// getRow and getColumn methods return integers for the row and column of the cell
// A = 1, B = 2, ... G = 7
if (cell.getRow() == 10 && cell.getColumn() == 7) {
// If its the right cell, copy to the other cell
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CopyData");
// set the value of the desired cell in the target sheet
target.getRange("T12").setValue(cell.getValue());
}
}
Third, save the script
Save the script, reload the file, and test it out.
If your tabs are on different spreadsheets
Change this line:
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CopyData");
to this:
var target = SpreadsheetApp.openById("FILE_ID").getSheetByName("CopyData");
and insert the file ID for the target spreadsheet where I've written FILE_ID.
You will also need to use an "installed trigger", since a simple trigger cannot open a remote spreadsheet. To do this, change the name (so it is no longer a simple trigger function), and follow the steps here

Google Sheets: noticing and acting upon new table added and table renamed

The title is the question.
I'm working on a spreadsheet in Google Drive where we got a central sheet named Name registry with names of the people who ever visited some place where notebooks are left at for them to write their name down on. The sheet calculates various stats, too.
The sheets for different places will be named as whatever the specific notebooks were named. The name registry saves us writing and fixing by referencing the cell of a particular name.
I'm sure there won't be anywhere like 255 sheets (which should be a lifted limit in the new Sheets), but I came across an idea how to have it automated:
1) Optionally a new sheet is added and the spreadsheet notices it.
2) Once either
2.1) the new sheet is renamed, the `Name registry` will auto-name one of the free columns as the renamed sheet; or
2.2) an old sheet is renamed, the sheet's old column is renamed in the `Name registry`
I did check on things, but the documentation is, frankly, brainlessly organized, so the only option is to use Google on its own resources.
For example, https://developers.google.com/apps-script/understanding_events implies it can't be done anyhow.
You should know that there is, indeed, a trigger that fires when inserting one sheet (I'm guessing that's what you call "table" in your question).
Re-check the docs for the spread sheet Change event, this event only fires if you use an installed trigger. To use it (copied from the docs)
Open or a create a new spreadsheet.
Click the Unsaved spreadsheet dialog box and change the name.
Choose Tools > Script Editor and write the function you want to run.
Choose Resources > Current project's triggers. You see a panel with the message No triggers set up. Click here to add one now.
Click the link.
Under Run, select the function you want executed by the trigger (for example myFunction).
Under Events, select From Spreadsheet
From the next drop-down list, select On Change.
Click Save.
Please notice that this trigger is not the same as the simple onEdit one.
Also, bear in mind that your callback function must receive the event parameter:
function myFunction(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getSheetByName("Sheet1").getRange("A1").setValue(e.changeType);
}
You'll see that there are different types of changes: EDIT, INSERT_ROW, INSERT_COLUMN, REMOVE_ROW, REMOVE_COLUMN, INSERT_GRID, REMOVE_GRID, OTHER. You're looking for INSERT_GRID for inserting new sheet and OTHER for renaming it.
Having said that, the problem here will be detecting the rename of one sheet as its an OTHER type, so you may easily trigger the function when you don't really want to do it (for example changing background color, also fires the onChange event with OTHER edit type). My advice is that you don't try to detect the createion/change but create a custom menu entries for handling the creation and renaming of sheets:
function onOpen()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "Create sheet", functionName: "createNewSheet"},
{name: "Rename current Sheet", functionName: "renameCurrentSheet"}
];
ss.addMenu("Custom operations", menuEntries);
}
function createNewSheet()
{
// create the new sheet
// also execute the logic you want when new sheet is created
}
function renameCurrentSheet()
{
// rename current sheet
// also execute the logic you want when sheet is renamed
}
Hope I put some light to your problem.

Custom function throws a "You do not have the permission required to setValue" error

I am trying to set some value to a cell in a Google Spreadsheet:
function exampleFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range1 = sheet.getRange("A1");
var value1 = range1.getValue();
value1+=1;
range1.setValue(2);
return value1;
}
If I am trying to affect a cell with this function, this error appears:
You do not have the permission required to setValue. (line 10, file "ddd")
Do you know how I could make that possible? I actually want the affected cell to take the value of the cell A1 and increase the value of A1 by 1.
from the documentation :
Custom functions return values, but they cannot set values outside the cells they are in. In most circumstances, a custom function in cell A1 cannot modify cell A5. However, if a custom function returns a double array, the results overflow the cell containing the function and fill the cells below and to the right of the cell containing the custom function. You can test this with a custom function containing return [[1,2],[3,4]];.
reference : Custom Functions in Spreadsheets
It looks that you are using the above function as a custom function, in other words, it is called by cell formula on the Google Sheets UI, in the following way:
=exampleFunction()
Custom functions in Google Sheets have limitations like they can't be used to call Google Apps Script services that require permissions. The workaround is to use another mean to call the function:
Run it from the Google Apps Script Editor
Use a custom menu
Use a trigger
Also they could be called from dialogs and sidebars, Google Apps Script Web apps and by using the Google Apps Script execution API
It's just a little different than what we programmers think.
You can use setFormula in a Macro but not in a custom function.
Just create a simple macro from Tools > Macros > Record Macro, and then open the Script editor and change the Macro's code to your code...
Here is my Macro's code:
function SetFormula() {
var spreadsheet = SpreadsheetApp.getActive();
var formulaValue = spreadsheet.getRange('formulaText').getValue().toString();
spreadsheet.getRange('total').setFormula(formulaValue);
return formulaValue;
};
Then, to run your macro automatically (you can run that manually from Tools > Macros > YOUR-MACRO-NAME), just create a trigger as follows:
Open the Script Editor:
Then go to Triggers from the left side panel and tap on Add Trigger button:
Finally, create the trigger, select your Macro from the list (mine is SetFormula), select the Event Source as From SpreadSheet, the Event Type to On Edit, and save it.
That's it!
I named my ranges as FormulaText and total to be more flexible.
you can do that from here:
Custom functions do have permission limitations as noted above. They can run with a custom menu or you can insert an image and assign a custom script to it to use it like a button.
Using a Trigger is another way to accomplish something like this example, which makes it automatic.
A simple trigger in an App Script such as onSelectionChange(e) works without running into the permissions issue of putting a custom function into a cell. This trigger is newer than what was available in the original post. In the simple example below, cell A1 will turn white with an even integer and red with anything else. Granted, the speed at which the triggers fire may vary. It's not always as instantaneous as one might expect.
function onSelectionChange(e) {
const sheet = SpreadsheetApp.getActive()
var value1 = sheet.getRange("A1").getValue()
if(value1 % 2 == 0) {
sheet.getRange("A1").setBackground("#FFFFFF") //white
} else {
sheet.getRange("A1").setBackground("#FF0000") //red
}
}