Custom Number Format for Number Range in Cell - google-sheets

I have a cell that contains a number range, say 50-60 that I would like to apply custom number formatting to. Ideally, I'd like to be able to format that to output 50Hz-60Hz.
The syntax that works for a regular integer is #"Hz", but I can't find an in-built way to do this for dashed ranges, and I suspect it isn't possible.

Answer:
As you suspected, this isn't possible to do.
More Information:
As Tanaike has said in his above comment, the cell input 50-60 is a string - purely read as text as it contains the - character. Resultantly, Sheets does not have the functionality to use Number formatting to change the way this is displayed.
(Kind of) Workarounds:
Disclaimer: These suggestions are not perfect workarounds and depending on how the data in the cells is processed elsewhere in the sheet, these may not work. They do however provide solutions if you are looking to affect the UI only.
Workaround 1: Using a custom Number Format
You can use the format ##"Hz-"##"Hz" which will display 50Hz-60Hz for the example you give, if the input of the cell is 5060 rather than 50-60. You will however need to change the format to contain three # characters if the frequency range of the cell goes above 100:
##"Hz-"##"Hz" will make the number 5060 display as 50Hz-60Hz
###"Hz-"###"Hz" will make the number 120130 display as 120Hz-130Hz
####"Hz-"####"Hz" will make the number 14201430 display as 1420Hz-1430Hz
Workaround 2: Using an onEdit(e) Trigger
If inputting the - yourself is important, then you can use an onEdit() Apps Script trigger to change the format of the cell to include the Hz unit after-the-fact.
For this workaround, I will assume that the column your frequency ranges are in is column C.
From the Tools > Script editor menu item, you can create a function with the following code:
function onEdit(e) {
if (e.range.getColumn() != 3) {
return;
}
else {
var f = e.value.split("-");
e.range.setValue(f = f[0] + "Hz-" + f[1] + "Hz");
}
}
Make sure to change the value on the line if (e.range.getColumn() != 3) to be whichever column your frequency ranges are: this example uses the value 3 because the column is assumed to be column C, but column D would be 4, E would be 5, etc.
Save the script with the save icon, press the run button (►), and confirm the authentication of running the script.
This will automatically run whenever a value like 50-60 is inputted into column C, and will change to display 50Hz-60Hz instead.
Workaround 3: Using a Custom Function
Google Sheets allow you to write custom formulae that work in a similar way to the built in formulae like =SUM() or =COUNT().
Following the same steps as in workaround 2 to open the Script editor, create the following function:
function hertzify(f) {
f = f.split("-");
return f[0] + "Hz-" + f[1] + "Hz";
}
This does a similar thing as workaround 2, but instead of automatically changing the values of whatever is in a specific column, you call the function by entering the following formula in a cell:
=HERTZIFY("50-60")
This will change the cell's display value to 50Hz-60Hz like before.
You can also use this on other cells; for example if cell C3 has the text 120-130 and in cell D3 you input =HERTZIFY(C3), then D3 will display 120Hz-130Hz.
Feature Request:
As the above workarounds either process the cell data as if they are text or require the number to be formatted in a specific way, they might not be perfect workarounds for all situations.
In this case I suggest filing a feature request with Google for the ability to define a number format for a range of values in a specific cell.
You can do this by either following the Help > Report a problem menu item from the Google Sheets user interface, or make a feature request on Google's Issue Tracker asking to implement this as a feature. The link to the Issue Tracker is here
References:
Format numbers in a spreadsheet - Computer - Doc Editors Help
Simple Triggers | Apps Script | Google Developers
Custom Functions in Google Sheets | Apps Script | Google Developers
Google's Issue Tracker

Related

How do I use ARRAYFORMULA and IF to apply a script to an entire column in Google Sheets?

I have little to no coding knowledge, so apologies if the solution is too obvious!
I am trying to add a Last Modified column to a Google Sheets file. To do this, I am using an AppScript function with the following code:
function setTimestamp(x) {
if(x != ""){
return new Date();
}
}
This works fine when I use setTimestamp(x) in my file. However, I am combining this with a Zapier action that creates a new row whenever new media is added. Every time a new row is created, any existing formulas are removed.
I assume I need to use ARRAYFORMULA to apply the setTimestamp formula to newly-created rows, but it must only apply to rows that aren't blank.
I have tried the following:
={"Last Modified";ARRAYFORMULA(setTimestamp(A2:A))} -> Only worked on first row
={"Last Modified";ARRAYFORMULA(B2:B=setTimestamp(A2:A))} -> Broke the file
={"Last Modified";ARRAYFORMULA(IF(A2:A)=1,setTimestamp(A2:A),"")} -> Expected 1 argument, got 3
Is there a way I can combine the IF into the script or a better way to solve the problem?
A public version of my file is available here: https://docs.google.com/spreadsheets/d/13zkVRPr2Wh5bHjCT8cenInHnBk7qkMkuEMdwUxC_cRU/edit?usp=sharing
All data is dummy data and stock photos.
Unfortunately, arrayformula does not function as an array map function for custom functions. (Even for native functions where you may expect it to work that way, it does not always, sadly.)
To handle array range, we need the custom function to handle array range directly. That also limits the number of individual calls to custom functions, which materially saves execution time.
To handle array range, there are 2 ways. I'll comment on both.
Array range directly as input of custom function
If the input is a single cell, it is read directly
If the input range spans more than a single cell, the data is read as nested lists: a list of lists of rows.
For example, A1 will be read as the data in A1. A1:B2 will be read as [[A1, B1], [A2, B2]].
You can remember it as columns of rows.
As for the input data format, numbers are taken without the display format. Texts are taken as strings.
If output is an array range, the result will automatically expend.
Thus, in your example, in B2 you can almost do
=setTimestamp(A2:A)
where setTimestamp() has been modified to
function out = setTimestamp(arr) {
out=Array(mat.length);
for (i=0;i<mat.length;i++){
j=0
if(arr[i][j] != ""){
out[i]=new Date();
}
}
return out
}
For more details, see the official help page. (Over the years, more details have become available.)
Almost, but not quite. For your direct question, above provides the answer. However, you seem to have an implicit requirement that your custom function is executed every time a new URL is found. Be careful that what happens here is that every time Google Sheet updates cell content, a new Date() is created and outputted.
Array range read within custom function
Since you know your URLs are in A2:A, and you want the output of your custom function to be B2:B, you can read and modify those ranges directly within your custom function via the Range Class.
In this route, you may find getLastRow(), getLastColumn() in Sheet and getNextDataCell() in Range convenient.
When you need to execute your custom function, you can run it manually or add onEdit() trigger to your custom function. (But onEdit() itself can mean substantial UI lag when using the sheet. It's usually more appropriate for sheets that parse external data automatically. See other triggers in the link for motions.)
In your example, you can almost do
function setTimestamp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var row=1;
var cell = sheet.getRange(row,1).getValue();
while (row<=lastRow) {
if(cell.getValue() != ""){
sheet.getRange(row,2).setValue(new Date());
}
cell = sheet.getRange(row,1,lastRow).getNextDataCell(SpreadsheetApp.Direction.DOWN);
row=cell.getRow();
}
}
which will scan for all URLs in A2:A and write current time to B2:B when executed.
Again, your example implicitly points to updating only when a new URL is found. So be careful about that. Use triggers as needed.
As for the need to place formula in B1, you can (and should) reference the output of your other application in a different sheet so that you or a different application of yours can edit without conflict.
Thus, for what was asked, we have everything.

How to make google sheet formula just calculate once?

I have 3 rows in my Google sheet, that is stock, price, and total. so, I just use "multiple" formula for stock and price then put the value into the total row. but I don't want total row get an update or change the value whenever I change stock value.
Can someone help me?
Assuming you want cell A1 to only calculate its value once, you can put the following in cell A1. This tells the cell to just use its existing value if there is one (and it's not 0), otherwise run the formula.
=IF(A1<>0, A1, formula())
Since the cell is referencing itself, you will need to enable iterative calculation in File > Spreadsheet settings > Calculation.
I use something like the following for historical Google Finance data, since the value is never going to change, and sometimes Google Finance randomly returns an error. This will only run the GOOGLEFINANCE() formula until it returns a non-zero value without erroring.
=IF(IFERROR(A1)<>0, A1, GOOGLEFINANCE(...))
Google Sheets is not build to operate in such a manner. The most simple and fastest solution is to calculate what you need and then use CTRL + C and repaste with CTRL + SHIFT + V
use the copy paste value option per https://www.ablebits.com/office-addins-blog/google-sheets-convert-formulas-values/
Initially I was going with Grayson's solution, but this
=IF(IFERROR(A1)<>0, A1, GOOGLEFINANCE(...))
Places a FALSE on the cell until the result is placed.
I needed that to be empty, i.e. "" as whatever different from that (cell <>"") would trigger another cell to do another query/request.
I also needed the formula to be run depending on the trigger (the url in another cell). In this formula, if the trigger is placed after the formula has run, it won't trigger it.
So I have something like
=IF(AND(IFERROR(E53)<>0,E53<>""),E53, if(D53<>"",IMPORTDATA(D53),""))
<Update 2022-11>
While using this in arrayformula I noticed something that could be wrong.
IFERROR(E53)<>0 # Doesn't make sense.
It should be
NOT(ISERROR(E53))
And the whole thing in arrayformula (careful with AND/OR )
=ARRAYFORMULA( IF( NOT(ISERROR(E2:E)) * (E2:E<>"") ,E2:E, 'SOMETHING ELSE' ) )
I leave both versions in case someone spots errors in any of them.
<End of update 2022-11>
Explanation (It took me a while to understand it, so I could extend it):
D53 = myself
If I am nothing ("") or I am in Error (importdata not yet completed)
then
I am the result of -> If(D53<>"",IMPORTDATA(D53),"")
Which is:
If the cell before me is something different from "", run the importdata
with that cell as url, otherwise I am "" (nothing)
This achieves the goal of running the formula only once if the trigger is valid (the url on the cell before is there). Once the result is placed, it won't change.
If for whatever reason you need it to run again, you have to remove the formula and place it again.
Notes: If cellX has the result of an external fetch (IMPORTDATA for example) and on cellY=cellX, while on cellX you see "Loading ..." on cellY you will see a 0 (zero). I believe that explains why the other solution was comparing with 0.

How To Transfer A Value From One Cell To Another In Google Sheets

I have an interesting situation where every second column in my Google Sheet represents the amount of items at various stations. What I'm trying to accomplish is to automatically adjust the amounts of these values when a transfer value is entered into a cell between these columns.
For example, here are the first few cells on the sheet:
Cell B5: Data entered here should be added to the value in C5, then clear
Cell C5: Displays AMOUNT C
Cell D5: Data entered here should be subtracted from value in C5, added to the value in E5, then clear
Cell E5: Displays AMOUNT E
See this attached example. Values are added to the yellow cells, and what they need to be doing to their adjacent cells is described above each one.
Some examples:
Adding the number 5 to Cell B5 needs to change the value of C5 from 72 to 77, and then B5 needs to clear.
Adding the number 12 to J6 needs to change the value of I6 from 36 to 24, the value of K6 from nothing to 12, and then J8 needs to clear.
Adding the number 32 to R5 needs to change the value of Q5 from 41 to 9, the value of S5 from 1 to 3, and then R5 needs to clear.
Adding the number 5 to Cell V6 needs to change the value of U6 from 5 to nothing, and then V6 needs to clear.
I would prefer using formulas, and since I can avoid circular reference errors by going to File/Spreadsheet Settings/Calculation and changing the Max number of iterations to 1 (Threshold at 0.05), I can use a formula like =C5+B5 in Cell C5. Is there a way to add to this formula so that B5 is cleared after its value is added to C5? And how would I then add the functionality of subtracting a value entered in D5 from C5, and then clear D5 as well?
Does anyone have any ideas? It would be much appreciated!
I think this onEdit() trigger is what you need. Also see the documentations on Apps Script and Spreadsheet App.
For example, in your sheet, an implementation for requirement 1 can be as follows.
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var tab = sheet.getSheetByName('Item Transferring');
sheet.setActiveSheet(tab); \\<---
var cell = e.range;
user_input = cell.getValue();
if (cell.getRow() == 5 && cell.getColumn() == 2){ \\ it's probably best that you read the row and column indices of B5 and C5 from a helper tab/sheet in case you decide to move B5 and C5 later.
var cell2 = tab.getRange(5,3);
cell2.setValue(cell2.getValue()+user_input);
cell.clear({contentsOnly: true});
SpreadsheetApp.setActiveRange(cell); \\<--- use this in conjunction with setActiveSheet. Either use both or use neither.
}
}
Set up an onEdit trigger for the function onEdit, and the desire effect will occur.
Note that in your scenarios, the edits are always on single cells. Sometimes, edits can occur to multiple cells simultaneously. Make sure there is no ambiguity in your interface. Also the protect function of Google Sheet may come in handy.
Comment:
Do try to create a different interface where you are not as reliant on change per edit. You can easily run into problems with synchronization. A final sheet that does not take a lot of inputs frequently may be fine. It nevertheless slows down your regular activities.
As well, you should probably have a cell that displays the most recent edit.
EDIT: regarding generalizing the code and applying the above example, you can do something like this:
function process_input(row_input,col_input,input,output,func){
// row_input and col_input are the cell indices you are watching
// input is the range object that the edit trigger passes in
// output is the range object that contains the cell you want your edit to happen to
// func contains the formula you want in the output cell
if (input.getRow()==row_input,input.getColumn()==col_input){
output.setValue(func(input,output));
input.clear({contentsOnly: true});
}
}
As an example that can apply to the rest of your problems, your scenario 1 from earlier would require a function as below. Be careful that a user input can be accidentally non-numeric.
function update_add(input,output){
if (!isNaN(input.getValue()) && isFinite(input.getValue()){
return input.getValue()+output.getValue()
}else{
return output.getValue()
}
}
You would write a simple function like the above for every kind of update you want to have. To put it all together, using your scenario 1 as an example, which is to monitor B5 and update B6 using add recipe, you would do
function onEdit(e){
...
output = tab.getRange(5,3);
process_input(5,2,e.range,output,update_add)
}
As mentioned earlier, it's better for you to pull the indices of the cells you are watching from a separate sheet -- as oppose to hardcoding the numbers 5, 3, 2. To implement a full solution for all of your scenarios, you can simply loop through all the cells you need to watch by update method and then loop through all possible update methods.
You do need to implement the exact code yourself. On Stackoverflow, we only discuss methods and how they work. We discuss using minimally self-contained examples. We can't hand codes to complete people's projects. That won't be a sustainable kind of interaction.
The intended take-away for you from this answer are the various Apps Script utilities linked and described with sample codes and the advices on generalization. That's it. If there are questions about a utility, please read the linked documents; and if you still have questions after, open another post with a specific focus.

Counting hyperlinks on Google Sheets formula

How do you count hyperlinked numbers within a simple Google Sheet formula? Currently the =SUM(F6:CS6) (for example) does not count cells with hyperlinks.
This requires a custom function, because built-in functions cannot detect what formulas are entered in other cells, they can only access values. The discussion at Google Product Forum confirms this. There is already a custom function posted there, but I wrote another one, a bit shorter and not case-sensitive:
function countLinks(rangeNotation, range) {
var formulas = SpreadsheetApp.getActiveSheet().getRange(rangeNotation).getFormulas();
return formulas.reduce(function(acc, row) {
return acc + row.reduce(function(acc, formula) {
return acc + (/^=HYPERLINK/i.test(formula) ? 1 : 0);
}, 0);
}, 0);
}
Usage example: =countlinks("A2:E10", A2:E10). Range notation has to be passed in as a string, because the function needs the range, not the values. But this also means it needs the second parameter to be recalculated in case something changes in the referenced range.
Warning: this only counts the hyperlinks created with hyperlink formula. It will not detect the links created by pasting rich text into a cell. Those links (which really should never be created) are not detectable with Google Apps Script at present.
If you dont want to use scripts and just want a formula, this might serve you: Countifs in Google Sheets with various 'different than' criteria in same row adds +1 value
Particularly, the answer about the function COUNTA might be what you are looking for.
Try to set the format of the column you want to sum to numbers.

Is there a Google Sheets formula to put the name of the sheet into a cell?

The following illustration should help:
Here is what I found for Google Sheets:
To get the current sheet name in Google sheets, the following simple script can help you without entering the name manually, please do as this:
Click Tools > Script editor
In the opened project window, copy and paste the below script code into the blank Code window, see screenshot:
......................
function sheetName() {
return SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
}
Then save the code window, and go back to the sheet that you want to get its name, then enter this formula: =sheetName() in a cell, and press Enter key, the sheet name will be displayed at once.
See this link with added screenshots: https://www.extendoffice.com/documents/excel/5222-google-sheets-get-list-of-sheets.html
You have 2 options, and I am not sure if I am a fan of either of them, but that is my opinion. You may feel differently:
Option 1: Force the function to run.
A function in a cell does not run unless it references a cell that has changed. Changing a sheet name does not trigger any functions in the spreadsheet. But we can force the function to run by passing a range to it and whenever an item in that range changes, the function will trigger.
You can use the below script to create a custom function which will retrieve the name:
function mySheetName() {
var key = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
return key;
}
and in the cell place the following:
=mySheetName(A1:Z)
Now if any value in a cell in that passed range changes the script will run. This takes a second to run the script and sets a message in the cell each time any value is changed so this could become annoying very quickly. As already mentioned, it also requires a change in the range to cause it to trigger, so not really helpful on a fairly static file.
Option 2: Use the OnChange Event
While the run time feels better than the above option, and this does not depend on a value changing in the spreadsheet's cells, I do not like this because it forces where the name goes. You could use a Utilities sheet to define this location in various sheets if you wish. Below is the basic idea and may get you started if you like this option.
The OnChange event is triggered when the sheet name is changed. You can make the code below more sophisticated to check for errors, check the sheet ID to only work on a given sheet, etc. The basic code, however, is:
function setSheetName(e) {
var key = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('K1').setValue(key);
}
Once you have saved the code, in the script editor set the Current Project's On Change Trigger to this function. It will write the sheet name to cell K1 on any change event. To set the trigger, select Current project's triggers under the Edit menu.
If you reference the sheet from another sheet, you can get the sheet name using the CELL function. You can then use regex to extract out the sheet name.
=REGEXREPLACE(CELL("address",'SHEET NAME'!A1),"'?([^']+)'?!.*","$1")
update:
The formula will automatically update 'SHEET NAME' with future changes, but you will need to reference a cell (such as A1) on that sheet when the formula is originally entered.
Not using script:
I think I've found a stupid workaround using =cell() and a helper sheet. Thus avoiding custom functions and apps script.
=cell("address",[reference]) will provide you with a string reference (i.e. "$A$1") to the address of the cell referred to. Problem is it will not provide the sheet reference unless the cell is in a different sheet!
So:
where
This also works for named sheets. Then by all means adjust to work for your use case.
Source: https://docs.google.com/spreadsheets/d/1_iTD6if3Br6nV5Bn5vd0E0xRCKcXhJLZOQqkuSWvDtE/edit#gid=1898848593
EDIT:
I've added another workaround in the document that makes use of =formulatext() and some traditional text functions. By referencing to a cell in the current sheet using it's full address, i.e. Sheet1A1 you are able to use formulatext() to extract only the sheet name.
Here is my proposal for a script which returns the name of the sheet from its position in the sheet list in parameter. If no parameter is provided, the current sheet name is returned.
function sheetName(idx) {
if (!idx)
return SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
else {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var idx = parseInt(idx);
if (isNaN(idx) || idx < 1 || sheets.length < idx)
throw "Invalid parameter (it should be a number from 0 to "+sheets.length+")";
return sheets[idx-1].getName();
}
}
You can then use it in a cell like any function
=sheetName() // display current sheet name
=sheetName(1) // display first sheet name
=sheetName(5) // display 5th sheet name
As described by other answers, you need to add this code in a script with :
Tools > Script editor
An old thread, but a useful one... so here's some additional code.
First, in response to Craig's point about the regex being overly greedy and failing for sheet names containing a single quote, this should do the trick (replace 'SHEETNAME'!A1 with your own sheet & cell reference):
=IF(TODAY()=TODAY(), SUBSTITUTE(REGEXREPLACE(CELL("address",'SHEETNAME'!A1),"'?(.+?)'?!\$.*","$1"),"''","'", ""), "")
It uses a lazy match (the ".+?") to find a character string (squotes included) that may or may not be enclosed by squotes but is definitely terminated by bang dollar ("!$") followed by any number of characters. Google Sheets actually protects squotes within a sheet name by appending another squote (as in ''), so the SUBSTITUTE is needed to reduce these back to single squotes.
The formula also allows for sheet names that contain bangs ("!"), but will fail for names using bang dollars ("!$") - if you really need to make your sheet names to look like full absolute cell references then put a separating character between the bang and the dollar (such as a space).
Note that it will only work correctly when pointed at a different sheet from the one that the formula resides! This is because CELL("address" returns just the cell reference (not the sheet name) when used on the same sheet. If you need a sheet to show its own name then put the formula in a cell on another sheet, point it at your target sheet, and then reference the formula cell from the target sheet. I often have a "Meta" sheet in my workbooks to hold settings, common values, database matching criteria, etc so that's also where I put this formula.
As others have said many times above, Google Sheets will only notice changes to the sheet name if you set the workbook's recalculation to "On change and every minute" which you can find on the File|Settings|Calculation menu. It can take up to a whole minute for the change to be picked up.
Secondly, if like me you happen to need an inter-operable formula that works on both Google Sheets and Excel (which for older versions at least doesn't have the REGEXREPLACE function), try:
=IF(IFERROR(INFO("release"), 0)=0, IF(TODAY()=TODAY(), SUBSTITUTE(REGEXREPLACE(CELL("address",'SHEETNAME'!A1),"'?(.+?)'?!\$.*","$1"),"''","'", ""), ""), MID(CELL("filename",'SHEETNAME'!A1),FIND("]",CELL("filename",'SHEETNAME'!A1))+1,255))
This uses INFO("release") to determine which platform we are on... Excel returns a number >0 whereas Google Sheets does not implement the INFO function and generates an error which the formula traps into a 0 and uses for numerical comparison. The Google code branch is as above.
For clarity and completeness, this is the Excel-only version (which does correctly return the name of the sheet it resides on):
=MID(CELL("filename",'SHEETNAME'!A1),FIND("]",CELL("filename",'SHEETNAME'!A1))+1,255)
It looks for the "]" filename terminator in the output of CELL("filename" and extracts the sheet name from the remaining part of the string using the MID function. Excel doesn't allow sheet names to contain "]" so this works for all possible sheet names. In the inter-operable version, Excel is happy to be fed a call to the non-existent REGEXREPLACE function because it never gets to execute the Google code branch.
I have a sheet that is made to used by others and I have quite a few indirect() references around, so I need to formulaically handle a changed sheet tab name.
I used the formula from JohnP2 (below) but was having trouble because it didn't update automatically when a sheet name was changed. You need to go to the actual formula, make an arbitrary change and refresh to run it again.
=REGEXREPLACE(CELL("address",'SHEET NAME'!A1),"'?([^']+)'?!.*","$1")
I solved this by using info found in this solution on how to force a function to refresh. It may not be the most elegant solution, but it forced Sheets to pay attention to this cell and update it regularly, so that it catches an updated sheet title.
=IF(TODAY()=TODAY(), REGEXREPLACE(CELL("address",'SHEET NAME'!A1),"'?([^']+)'?!.*","$1"), "")
Using this, Sheets know to refresh this cell every time you make a change, which results in the address being updated whenever it gets renamed by a user.
I got this to finally work in a semi-automatic fashion without the use of scripts... but it does take up 3 cells to pull it off. Borrowing from a bit from previous answers, I start with a cell that has nothing more than =NOW() it in to show the time. For example, we'll put this into cell A1...
=NOW()
This function updates automatically every minute. In the next cell, put a pointer formula using the sheets own name to point to the previous cell. For example, we'll put this in A2...
='Sheet Name'!A1
Cell formatting aside, cell A1 and A2 should at this point display the same content... namely the current time.
And, the last cell is the part I'm borrowing from previous solutions using a regex expression to pull the fomula from the second cell and then strip out the name of the sheet from said formula. For example, we'll put this into cell A3...
=REGEXREPLACE(FORMULATEXT(A2),"='?([^']+)'?!.*","$1")
At this point, the resultant value displayed in A3 should be the name of the sheet.
From my experience, as soon as the name of the sheet is changed, the formula in A2 is immediately updated. However that's not enough to trigger A3 to update. But, every minute when cell A1 recalculates the time, the result of the formula in cell A2 is subsequently updated and then that in turn triggers A3 to update with the new sheet name. It's not a compact solution... but it does seem to work.
To match rare sheets names like:
Wow!
Oh'Really!
''!
use the formula:
=SUBSTITUTE(REGEXEXTRACT(CELL("address";Sheet500!A1);"'?((?U).*)'?!\$[A-Za-z]+\$\d+$");"''";"'")
or
=IF(NOW();SUBSTITUTE(REGEXEXTRACT(FORMULATEXT(A1);"='?((?U).*)'?![A-Za-z]+\d+$");"''";"'")) if A1 is formula reference to your sheet.
if you want to use build-in functions:
=REGEXEXTRACT(cell("address";'Sheet1'!A1);"^'(.*)'!\$A\$1$")
Explanation:
cell("address";'Sheet1'!A1) gives you the address of the sheet, output is 'Sheet1'!$A$1. Now we need to extract the actual sheet name from this output. I'm using REGEXEXTRACT to match it by regex ^'(.*)'!\$A\$1$, but you can either use more/less specific regex or use functions like SUBSTITUTE or REPLACE

Resources