Google Sheets Scripts: Can you get a range from a rangeId? - javascript

My google sheet has a cell on sheet1 that contains a link to a cell on sheet2. In my function, I am able to get the link url, but cannot figure out how to get a range from the rangeId:
var link = generatorSheet.getRange(currRow, 2)
var linkUrl = link.getRichTextValue().getLinkUrl()
Logger.log(linkUrl) // linkUrl = "rangeid=1843553975"
I've tried using getRangeByName and various other functions but keep getting a null value back, not a Range object.
Thanks in advance!
Edit: My overall goal in this is to iterate over each row in sheet1, where each cell in column 2 links to a cell in sheet2. I need to take the value from the cell in sheet2 and copy it into sheet3. In sheet1, there's a check box in column 1 of each row, so that's what I'm using to determine whether or not the linked to value will be copied. I'll have a button to kick off my function and populate sheet3, and it has to assume these links are already in place - they were done by hand prior

When you create an hyperlink to a range using the user interface, you are facing this issue. I think you may have to change the way of designing the hyperlink and try to define it by the formula
=hyperlink("#gid=123456789&range=A2","go to ...")
and then you will retrieve the range by
Logger.log(linkUrl.match(/(?<=range=).*/g))

For documentation purposes,
This is a url hash fragment:
#rangeid=1843553975
The id seems to be created, when inserting a link to a range using the user interface. This is distinctly different from a namedRange When clicked, it's appended to the url in the browser, i.e.,https://docs.google.com/spreadsheets/id/edit#rangeid=1843553975. Once appended, through onpopstate javascript event, the range linked to the id is highlighted in the browser/app.
NamedRanges has a similar workflow. It also provides a rangeid=<10 digit ID>. But, it also has a name attached to it. But even in this case, the rangeid is not retrievable, though Sheets API provides a obfuscated range id.
There was a feature request made to Google, but it was made obsolete, because of lack of response on the part of the requestor:
https://issuetracker.google.com/issues/162810351
You may create a new similar issue there with a link to this answer. Once created, link the issue here.
Other related trackers:
https://issuetracker.google.com/issues/129841094
https://issuetracker.google.com/issues/134986436

Related

Retrieving a row and adding it into another character sheet tab does not carry its properties

I am using Google-sheet as my database & google API request.
let rows: GoogleSpreadsheetRow = getSomeRows()
let row = rows.filter(...)
let index = (await this.memo.addRow(row)).rowIndex
addborders(this.memo, index, 0)
You can see the task are simple, Retrieve a row, store it into the variable and finaly add all the information at the end of another google sheet tab with the help of .addRow(). What I was expecting is for it to keep all its cells properties but what I got is a row added in RAW meaning there are no borders, padding etc so I resolve this by adding them manually with my small function I created called addborders.
The code is working perfectly fine but the only thing I couldn't find is how to retrieve the cell history.
If anyone knows how to do it, would be very much appreciated & maybe it could carry the formatting properties too so i don't have to use my addborders function anymore while using .addRow().
Best Regards.

Service Spreadsheet Time Out - Optimizing Macro script?

Context
I have a g-sheet that acts as a sort of "master sheet" in which everything pours into from a bunch of other outside spreadsheets that are all consistently being live updated throughout the day.
Each outside spreadsheet I connect, routes to its own tab within our master spreadsheet through importrange function
All those tabs then route to one master tab using row ID #'s - so that everyone can just work from that tab.
The Problem
In this master tab where everything lands, I have a macro sorting the rows to bring the most recent rows to the top, among other things to keep the data clean. As I connect more sheets over time, I add to the number in the macro to accommodate new rows.
Macro a couple days ago started throwing "Service Spreadsheet timed out while accessing document with id..." then the id is the id # of the master tab itself.
Know there is probably a lot smoother way to have this done without using a large bandwidth macro in place, but optimizing the script to best fit the use-case is far out of my experience level. The macro I have in place is as follows:
function MasterSormat2() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('D1').activate();
var criteria = SpreadsheetApp.newFilterCriteria()
.setHiddenValues([''])
.build();
spreadsheet.getActiveSheet().getFilter().setColumnFilterCriteria(4, criteria);
criteria = SpreadsheetApp.newFilterCriteria()
.build();
spreadsheet.getActiveSheet().getFilter().setColumnFilterCriteria(4, criteria);
criteria = SpreadsheetApp.newFilterCriteria()
.setHiddenValues([''])
.build();
spreadsheet.getActiveSheet().getFilter().setColumnFilterCriteria(4, criteria);
spreadsheet.getRange('A1:AP11001').activate();
spreadsheet.getActiveRange().offset(1, 0, spreadsheet.getActiveRange().getNumRows() - 1).sort({column: 4, ascending: false});
spreadsheet.getRange('A:AM').activate();
spreadsheet.getActiveRangeList().setFontFamily('Calibri')
.setHorizontalAlignment('left');
spreadsheet.getRange('P:S').activate();
spreadsheet.getActiveRangeList().setHorizontalAlignment('right');
spreadsheet.getRange('U:U').activate();
spreadsheet.getActiveRangeList().setHorizontalAlignment('right');
spreadsheet.getRange('AA:AG').activate();
spreadsheet.getActiveRangeList().setHorizontalAlignment('right');
spreadsheet.getRange('AL:AL').activate();
spreadsheet.getActiveRangeList().setHorizontalAlignment('right')
.setNumberFormat('"$"#,##0.00');
spreadsheet.getRange('D4').activate();
};
Can anyone possibly point me in the right direction here when it comes to improving this?
Thanks for any help that you can provide here, I look forward to learning further
Tl;Dr:
If you are able to record the macro again, consider to use Go to range or the name box instead of using your mouse to move from one place to anohter in your spreadsheet as each click on a sheet and range adds a statement that activates the corresponding sheet / range. The .activate() methods usually are a lot slower that other options.
The alternative to record the macro again is to remove statements like spreadsheet.getRange(harcoded_ref).activate() and replace statements like spreadsheet.getActiveRangeList() by, i.e.,
spreadsheet.getRange(hardcoded_ref),
spreadsheet.getRangeList(array_of_refs),
etc.
For this, you require some writing-code-skills, JavaScript knowledge and Spreadsheet Service (Class SpreadsheetApp) knowledge. In order to be able achive the best performance possible in Google Apps Script you should consider to use the Advanced Sheets Service, more specifically the spreadsheet.batchUpdate method.
Go to range
Try the following keyboard shortcuts
Windows
Mac
Ctrl + Alt + .Ctrl + Alt + ,
⌘ + Option + .⌘ + Option + ,
If the above keyboard shortcuts don't work for you or you are on a different operative system, to open the Go to side panel, click in the Help menu, then on the search box type Go to, then select Go to range. This will open a "side panel", type the cell reference, then press enter or click on >.
Help menu
Side panel
Name box
Writing code skills and JavaScript knowledge
I suggest you to spend some time on learning the JavaScript basics in order to be able to understand the recorded macro and adapt it to your needs.
Let say that you learned about primitives, objects, properties, classes, methods, literals and variables and understand the very basics of method chaining as the macro recorder used it a lot.
One of the things that you might find that will help to optimize the recorded macros is by assigning objects to variables. I.E. assign Class Sheet object corresponding to the active sheet to the variable named sheet:
var sheet = spreadsheet.getActiveSheet();
Then replace all the spreadsheet.getActiveSheet() by sheet.
In order to improve the performance of your recorded macro, also you should replace
spreadsheet.getRange(something).activate();
by
var rangeSomething = spreadsheet.getRange(something);
then replace the spreadsheet.getRange(something).chain1 before the following spreadsheet.getRange(something).activate(); by rangeSomething.chain1
If you find multiple likes like
spreadsheet.getRange('P:S').activate();
spreadsheet.getActiveRangeList().setHorizontalAlignment('right');
replace these lines by something like this:
var rangeList = spreadsheet.getRangeList(['P:S','U:U','AA:AG','AL:AL']);
rangeList.setHorizontalAlignment('right');
Related
GOOGLE script 'copyTo values only' does not work when the source is a function (e.g. NOW())

How do you extract urls from a column of cells containing linked text with numeric values in Google Sheets?

Edit/Update: This question is helpful for the specific use case where you are trying to extract urls from cells containing numeric values with linked URLs.
I have a spreadsheet with a column of cells copied from a website containing text with linked urls (i.e. the urls are not stored within a hyperlink formula). Here is a sample spreadsheet. I would like to extract the urls from the linked text (column A) into a new column in the spreadsheet.
Last year (2020) I found the custom function javascript code shared in the comments of this other StackOverflow question which worked well. However when I tried this with a new spreadsheet yesterday, this code was no longer working -- instead of returning the url, it returns nothing. There is no error message, it just returns an empty value.
Other things I've tried:
I was able to successfully retrieve the url in some cases by using this other javascript function:
function GETLINK(input) {
return SpreadsheetApp.getActiveSheet().getRange(input).getRichTextValue().getLinkUrl();
}
But it only worked correctly on cells where the text was not formatted as a number AND it requires entering the input as either a firm cell reference by putting it in quotes i.e. =getlink("A2") or by spelling out the address, i.e. =getlink(ADDRESS(ROW(A2), COLUMN(A2)))
I would like to figure out what has broken with the original code, which does not require these additional workarounds.
Explanation:
From the documentation, getRichTextValue() will return null if the value of the cell is not formatted as text. Most of the codes on the reference question use this method, so it returns error once getLinkUrl() is called. Thus the only workaround for this is to force text formatting by putting single quote before the cell value:
This should also work with linkURL().

How I get access to new intervals using "getNextDataRange()" of Selection Class?

I'm facing an error when I try to get values using the Method "getNextDataRange(Direction)" but I can't understand where I missed out.
first: I Opened another spreadsheet and selected the first sheet. I SET "B2" as initial current cell. Then I back to select the actual sheet to get access of "Selection" Class, so finally I can use "getNextDataRange":
Heres my code:
function ok() {
var sheets = SpreadsheetApp.openById("SHEET ID").getSheets()
var firstsheet = sheets[0].getRange("B2").activateAsCurrentCell().getSheet().getSelection().getNextDataRange(SpreadsheetApp.Direction.RIGHT).getValues
return SpreadsheetApp.getActiveSpreadsheet().getRange("B1").activate().setValue(firstsheet[0][0])
}
--
3. Then I wanna bring the values obtained in the range select by the getNextDataRange to import to my actual spreadsheet. Start from the range "B1" and taking the first line and the first column of the selection that I got previously.
The fallow error occours:
Cannot find getNextDataRange ((class)) method.
Someone has already faced that? I appreciate if someone could explain what detail I missed. I believe that is that doubt of many other too.

How to implement query function to use Google spreadsheet as a database?

I'm trying to use a spreadsheet as a database, where each sheet would be a table and the name of a person is used as a primary key (It seems not to be the best solution, but the good spreadsheet interface makes me prefer this solution rather than trying to use ScriptDB.)
And I want to do the following: When you select a name on a sheet and press a button on the menu I added, a function performs a search in another table and a screen and shows all the results of that query in the other table, showing properties records that only that table contains (later I want to add the possibility to generate a text file from a GDocs template).
My questions is:
1) Considering this screen/panel UI has a variable length (because the record number may vary in other tables), what is the best way to create this panel/UI in Google Apps Script? (I don't want to use the Logger.log because I want to add a button to convert the query into a file)
2) In addition to this solution (a search in the resulting 2D array):
function test(){ // to test the find function with an argument, 'any' in this case
var result = findItem('any');
if(result){Logger.log(result.getA1Notation())}else{Logger.log('no luck !')};
}
function findItem(item){
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = ss.getDataRange().getValues()
for(var n = 0;n<data.length;++n){
if(data[n].indexOf(item)>-1){ // this is a "strict" find, ie the value must be the entire search item. If you want to do partial match you should compare differently...
return (ss.getRange(n+1,data[n].indexOf(item)+1)); // if found return the range. note the +1 because sheets have 1 index while arrays have 0 index
}
}
return false;// if we come to the end of sheet without result...
}
There is an alternative method to perform queries like this?
THANKS for any help!
Create a UI instance. Then a scrollable panel inside a main panel is the best way of doing this and then using array's to search through the data. I typically create header body and footer panels with the body being scrollable

Categories