Accessing to sheet with gid - google-apps-script

sorry if my question is answered somewhere else, I've been looking for answers on google all afternoon but I'm still too newbie.
I'm trying to use Google spreadsheets' scripts to access to a different spreadsheet.
The only information I have is the spreadsheet's URL, where it has the key and the gid (some kind of chronological index for multi-sheet spreadsheets - only information i could find is here).
The sheet URL is something like https://docs.google.com/spreadsheet/ccc?key=abc123#gid=178
And the sheet it links to is the first sheet in the spreadsheet.
How do I find the sheet that maches the gid?
The following doesn't work, since it's based on the sheets' order, not the time they are created:
var ss = SpreadsheetApp.openById("abc123");
var sheet = ss.getSheets();
Browser.msgBox(sheets[178].getIndex());

I would do something simple like this:
var refSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SHEET NAME HERE");
var refSheetId = refSheet.getSheetId().toString();
And then append the refSheetId to the end of the getUrl string
var ssUrl = SpreadsheetApp.getActiveSpreadsheet().getUrl()+"#gid="+refSheetId;
Hope this helps!

This is how i do it:
function getSheetByGid(spreadsheet, gid){
gid = +gid || 0;
var res_ = undefined;
var sheets_ = spreadsheet.getSheets();
for(var i = sheets_.length; i--; ){
if(sheets_[i].getSheetId() === gid){
res_ = sheets_[i];
break;
}
}
return res_;
}
var sheet = SpreadsheetApp.openById("YOUR_SHEET_ID_HERE");
var seetWithGid = getSheetByGid(cpSheet, "YOUR_GID_HERE");

I know this is late, and may not be useful to original requester anymore. But I saw this question as I was working on the functionality, but ended up solving it myself later. You can look up a sheet by gid, and the fact that it doesn't change when you delete the sheet is a good thing.... if that's how you intend to use it. Basically I get all sheets, and loop through sheet names and sheet gid until the gid matches, and then I call the sheet by name.
Here's part of some code I use to take a row, from a aggregated sheet on edit, and push to the sub sheet as part of a two way sync. If you have just the gid and key stored, you can skip the steps i showed, and just reference those values. It seems to work very quick, it's a more complicated script then running from the workbook with the broken out sheets but both scripts take almost exactly 2 seconds to push, which is acceptable for me.
function Stest()
{
SpreadsheetApp.flush();
var sheet = SpreadsheetApp.getActiveSheet();
var r = sheet.getActiveRange();
var lastColumnRow = sheet.getLastColumn();
var activeRow = r.getRow();
var dataRange = sheet.getRange(activeRow,1,1,lastColumnRow);
var data = dataRange.getValues();
var sskeytemp = data[0][10].split("=")[1]; //the sheet url is in column k
var sskey = sskeytemp.split("#")[0]; //these first two steps get the sheet key
var ssid = data[0][10].split("gid=")[1]; //this last step gets the gid
var wrkbk = SpreadsheetApp.openById(sskey).getSheets(); //this gets the sheets from the workbook
for (var i = 0 ; i < wrkbk.length ; i++ ) {
if( ssid == wrkbk[i].getSheetId() ){ //this is to say if the spreadsheet gid, which is the gid of the sheet i know, matches the gid in the workbook from the link, thats the sheet i'm looking for
var ssname= wrkbk[i].getName();
var ss = SpreadsheetApp.openById(sskey).getSheetByName(ssname); //give me that sheet by name
var sslastColumn = ss.getLastColumn();
var sslastRow = ss.getLastRow();
var dataRange = ss.getRange(1,1,sslastRow,sslastColumn);
var data2 = dataRange.getValues(); //here's your data range, you can proceed freely from here

It is best not to rely on the gid for many reasons
A deleted sheet will not show up in your getSheets() method but will have accounted for in the gid
For example:
Create Sheet1 (by default, gid#1)
Create Sheet2 (gid#2)
Delete Sheet2
Create Sheet3 (gid#3)
The order will be jumbled up if the user decides to move sheets around in Spreadsheet UI. getSheets returns the sheets in the order they are arranged in the spreadsheet.
The only way you can get to the sheet is by its name. ALternatively, if you know of some content in the sheet, you can search through each sheet.

Alternatively this works for me perfect:
function _getSheetId() {
var getSpreadsheetURL = SpreadsheetApp.getActiveSpreadsheet().getUrl();
var mat = getSpreadsheetURL.match(/^(https:\/\/docs\.google\.com\/spreadsheets\/d\/)([a-zA-Z0-9]+\/)/g);
Logger.log(mat.toString()+"#gid="+SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetId());
}

Related

Fetch data from source sheet based on emails provided in the Emails tab in the target spreadsheet

I have a problem where I have two sheets. one sheet is the source spreadsheet and another is a target spreadsheet. The source spreadsheet has a source sheet has which is the master database and the target spreadsheet has the target where we want to fetch data from source sheet based on emails provided in the Emails tab in the target spreadsheet.
I want the following things to happen with a script and not with IMPORTRANGE or QUERY:
The target spreadsheet will have multiple copies so I want to connect the target spreadsheet with the source spreadsheet based on the source spreadsheet's id.
I want the email matches to be case insensitive so that the users of the target spreadsheet can type emails in any case.
The Emails can go up to 50 or let's say get the last row for that column.
It will be great if the script shows a pop up saying updated after it has fetched the data.
The source sheet might have data up to 15000 rows so I am thinking about speed too.
I have shared both of the spreadsheets with hyperlinks to their names. I am not really great at scripts so it will be helpful if you can leave comments in it wherever you feel like. I would truly appreciate your help.
Thanks in advance!
Script here:
function fetch() {
//get the sheets
var source_Ssheet = SpreadsheetApp.openById('19FkL3rsh5sxdujb6x00BUPvXEEhiXfAeURTeQi3YWzo');
var target_Ssheet = SpreadsheetApp.getActiveSpreadsheet();
//get the tabs
var email_sheet = target_Ssheet.getSheetByName("Emails");
var target_sheet = target_Ssheet.getSheetByName("Target Sheet");
var source_sheet = source_Ssheet.getSheetByName("Source Sheet");
//get ranges
var email_list = email_sheet.getRange("B2:B");
var target_sheet_range = target_sheet.getRange("A1:F100");
var source_sheet_range = source_sheet.getRange("A1:F100");
//get last rows
var last_email_name = email_list.getLastRow();
var last_target_sheet_range = target_sheet_range.getLastRow();
var last_source_sheet_range = source_sheet_range.getLastRow();
//start searching for emails
for (var i=3; i < last_email_name.length+1; i++)
{
for(varj=3; j< last_source_sheet_range.length+1; j++ )
{
if(source_sheet_range[j][3].getValue() == email_list[i][3].getValue())
{
//copy matches to target sheet
target_sheet.getRange((last_target_sheet_range + 1),1,1,10).setValues(master_sheet_range[j].getValues());
}
}
}
}
Several things
last_email_name and last_source_sheet_range are numbers - they do not have any length, this is why your first forloops are not working
You are missing a space in varj=3;
email_list[i][3].getValue() does not exist because email_list only includes B - that only one column. I assume you meant email_list[i][0].getValue()
ranges cannot be addressed with the indices [][], you need to retrieve the values first to have a 2D value range.
You email values in the different sheets do not follow the same case. Apps Script is case sensitive, to suee the == comparison you need to use the toLowerCase() method.
Also mind that defining getRange("B2:B") will include many empty rows that you don't need and will make your code very slow. Replace it through getRange("B2:B" + email_sheet.getLastRow());
Have a look here at the debugged code - keep in mind that there is still much room for improvement.
function fetch() {
//get the sheets
var source_Ssheet = SpreadsheetApp.openById('19FkL3rsh5sxdujb6x00BUPvXEEhiXfAeURTeQi3YWzo');
var target_Ssheet = SpreadsheetApp.getActiveSpreadsheet();
//get the tabs
var email_sheet = target_Ssheet.getSheetByName("Emails");
var target_sheet = target_Ssheet.getSheetByName("Target Sheet");
var source_sheet = source_Ssheet.getSheetByName("Source Sheet");
//get ranges
var email_list = email_sheet.getRange("B2:B" + email_sheet.getLastRow()).getValues();
var target_sheet_range = target_sheet.getRange("A1:F100").getValues();
var source_sheet_range = source_sheet.getRange("A1:F100").getValues();
var last_target_sheet_range = target_sheet.getLastRow();
//start searching for emails
for (var i=1; i < email_list.length; i++)
{
for(var j=1; j< source_sheet_range.length; j++ )
{
if(source_sheet_range[j][0].toLowerCase() == email_list[i][0].toLowerCase())
{
target_sheet.getRange((last_target_sheet_range + 1),1,1,6).setValues([source_sheet_range[j]]);
}
}
}
}

Google app script trigger not writing data to other sheets in same spreadsheet

I have the following app script associated with a Google Spreadsheet that is accepting data from a Google Form:
function writePatientData() {
var spreadsheet = SpreadsheetApp.openById("<spreadsheet id>");
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
//get last row in active/main sheet
var numRows = sheet.getLastRow();
//get last row of data
var last_row = sheet.getSheetValues(numRows, 1, 1, 23);
//get patientID (column V) in last row of sheet
var lastPatientID = sheet.getRange(numRows,3).getValue();
//find patient sheet based on patientID and make it active, then write to it
var patientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
var activePatientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
activePatientSheet.getRange(activePatientSheet.getLastRow()+1, 1,1,23).setValues(last_row);
}
What this script is doing is writing data (a row) to another sheet within this spreadsheet based on the the patientID (column V). This works as it should when I manually run the script. However, when I set a trigger to run this script (either onSubmit or edit) nothing happens. I created another function that just writes a message to the logs and set a trigger for that function and it works, so I think there is something in the script that is causing it to fail. Any ideas appreciated.
There are a few issues with your code. I tried to fix it while commenting each line I changed. Hopefully that is clear enough, please comment if you have any questions and I'll try to clarify.
function writePatientData() {
var spreadsheet = SpreadsheetApp.getActive(); //no need for id if the script is on the same spreadsheet
//var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
//setActiveSheet will not work from a trigger like on-form-submit (what if no-one has the sheet open, or multiple have)
var sheet = spreadsheet.getSheets()[0]; //if you want the first sheet, just get it, no need to "activate"
var numRows = sheet.getLastRow();
var last_row = sheet.getSheetValues(numRows, 1, 1, 23)[0]; //added [0] since it is just one row
//var lastPatientID = sheet.getRange(numRows,3).getValue(); //you already have this in memory
var lastPatientID = last_row[2]; //arrays are zero based, that's why 2 instead of 3
//btw, you mention column V, but this is actually C
//var patientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
//you already have the spreadsheet, no need to get it again
var patientSheet = spreadsheet.getSheetByName(lastPatientID);
//var activePatientSheet = spreadsheet.getSheetByName(lastPatientID); //this is the exact same as above, why?
patientSheet.appendRow(last_row); //appendRow is just simpler than getRange(getLastRow).setValues
}

How to make google sheets index from values in column and hyperlink those to sheets

I have a bunch of data I want to put in to multiple sheets, and to do it manually would take time and I would like to learn scripting too.
So say I have a sheet with the states in one column.
I would like to have a script make new sheets based off the values of that column, and make a hyperlink to those sheets, and sort the sheets alphabetically.
In each sheet, I need to have the A1 cell the same name as the sheet.
Here is an example of states
Any suggestions would be helpful
Edit:
This is code that can make sheets based on the values of the columns.
function makeTabs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var last = sheet.getLastRow();//identifies the last active row on the sheet
//loop through the code until each row creates a tab.
for(var i=0; i<last; i++){
var tabName = sheet.getRange(i+2,1).getValue();//get the range in column A and get the value.
var create = ss.insertSheet(tabName);//create a new sheet with the value
}
}
(note the "sheet.getRange(i+2,1" assumes a header, so pulls from the first column, starting on the second row)
I still need to:
Add a hyper link in the index sheet to the State's sheet: example: A2 on the Index sheet
would be =HYPERLINK("#gid=738389498","Alabama")
Also I need the A1 cell of the State's page to have the same info as
the index. example: Alabama's A1 cell would be =Index!A2
You could take a look at this script:
function createSheets(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var indexSheet = ss.getSheetByName('Index');
var indexSheetRange = indexSheet.getDataRange();
var values = indexSheetRange.getValues();
var templateSheet = ss.getSheetByName('TEMPLATE_the_state');
templateSheet.activate();
var sheetIds = [],
state,
sheetId,
links = [];
for (var i = 1 ; i < values.length ; i++){
state = values[i][0];
sheetId = undefined;
try{
var sheet = ss.insertSheet(state, {template: templateSheet});
SpreadsheetApp.flush();
sheet.getRange("A1:B1").setValues([['=hyperlink("#gid=0&range=A' +(i+1)+'","go back to index")',state]]);
sheetId = sheet.getSheetId();
}
catch (e) { Logger.log('Sheet %s already exists ' , sheet)}
sheetIds.push([sheetId,state]);
}
sheetIds.forEach(function(x){
links.push(['=HYPERLINK("#gid='+x[0]+'&range=A1","'+x[1]+'")']);
});
indexSheet.getRange(2,1,links.length,links[0].length).setValues(links) // in this case it is clear to us from the outset that links[0].length is 1, so we could have written 1
}
Note that in my version, I created a template sheet from which to base all the state sheets from. This wasn't what you asked for, but I wanted to see what it would do.
The resulting sheet is here: https://docs.google.com/spreadsheets/d/1Rk00eXPzkfov5e3D3AKOVQA2UdvE5b8roG3-WeI4znE/edit?usp=sharing
Indeed, I was surprised at how long it took to create the full sheet with all the states - more than 250 secs. I looked at the execution log, which I have added to the sheet in its own tab. There it is plain to see that the code is quick, but sometimes (why only sometimes, I don't know) adding a new tab to the spreadsheet and/or flushing the formulas on the spreadsheet is very slow. I don't know how to speed it up. Any suggestions welcome. (I could try the Google Sheets API v4, probably would be much faster ... but that is much more work and tougher to do.)

Improving Apps Script flexibility by using a column of sheet data instead of hard-coded IDs

Background: My coworkers originally each had a worksheet within the same Google Sheets file that makes a lot of calculations (and was getting unusable). Now, everyone has their own (known) Google Sheets file. To run the same calculations, we need to consolidate all that data into a master sheet (image ref below). We tried =importrange(...), but it's too heavy and breaks often (i.e., Loading... and other unfilled cells).
I've written some code to do this import, but right now its only manual: manually repeating the code and manually add the sheet IDs and changing the destrange.getRange(Cell range) each time. We have 80+ analysts, and fairly high turnover rates, so this would take an absurd amount of time. I'm new to Sheets and Apps Script, and know how to make the script use a cell as reference for a valid range or a valid ID, but I need something that can move a cell down and reference the new info.
Example:
Sheet 1 has a column of everyone Sheet ID
Script Pseudocode
get first row's id(Row 1), get sheet tab, get range, copies to active sheet's corresponding row(Row 1).
gets second row's id(Row 2), get sheet tab, get range, copies to active sheet's corresponding row (Row 2)
etc.
My script understanding is way to low to know how to process this. I have no idea what to read and learn to make it work properly.
function getdata() {
var confirm = Browser.msgBox('Preparing to draw data','Draw the data like your french girls?', Browser.Buttons.YES_NO);
if(confirm == 'yes'){
// I eventually want this to draw the ID from Column A:A, not hard-coded
var sourcess = SpreadsheetApp.openById('1B9sA5J-Jx0kBLuzP5vZ3LZcSw4CN9sS6A_mSbR9b26g');
var sourcesheet = sourcess.getSheetByName('Data Draw'); // source sheet name
var sourcerange = sourcesheet.getRange('E4:DU4'); // range
var sourcevalues = sourcerange.getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet(); //
var destsheet = ss.getSheetByName('Master Totals'); //
// This range needs to somehow move one down after each time it pastes a row in.
var destrange = destsheet.getRange('E4:DU4');
destrange.setValues(sourcevalues); // Data into destsheet
}
}
Any suggestions are greatly appreciated!
Thanks to tehhowch for pointing me in the right direction!
function getdata() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master Totals');
var confirm = Browser.msgBox('Drawing Data','Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO);
if(confirm =='yes'){
var lr = ss.getLastRow();
for (var i = 4; i<=lr; i++) {
var currentID = ss.getRange(i, 1).getValue();
var sourcess = SpreadsheetApp.openByUrl(currentID);
var sourcesheet = sourcess.getSheetByName('Data Draw');
var sourcerange = sourcesheet.getRange('E4:DU4');
var sourcevalues = sourcerange.getValues();
var destrange = destsheet.getRange('E' +i+':'+ 'DU'+ i);
destrange.setValues(sourcevalues);
I just had to learn how to use a variable loop.
Edit: thanks also to Phil for making my question more presentable!
Now that you've figured out one way to do it, I'll offer an alternative that uses batch methods (i.e. is much more time- and resource-efficient):
function getData() {
var wb = SpreadsheetApp.getActive();
var ss = wb.getActiveSheet();
var dest = wb.getSheetByName('Master Totals');
if (!dest || "yes" !== Browser.msgBox('Drawing Data', 'Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO))
return;
// Batch-read the first column into an array of arrays of values.
var ssids = ss.getSheetValues(4, 1, ss.getLastRow() - 4, 1);
var output = [];
for (var row = 0; row < ssids.length; ++row) {
var targetID = ssids[row][0];
// Open the remote sheet (consider using try-catch
// and adding error handling).
var remote = SpreadsheetApp.openById(targetID);
var source = remote.getSheetByName("Data Draw");
var toImport = source.getRange("E4:DU4").getValues();
// Add this 2D array to the end of our 2D output.
output = [].concat(output, toImport);
}
// Write collected data, if any, anchored from E4.
if(output.length > 0 && output[0].length > 0)
dest.getRange(4, 5, output.length, output[0].length).setValues(output);
}
Each call to getRange and setValues adds measurable time to the execution time - i.e. on the order of hundreds of milliseconds. Minimizing use of the Google interface classes and sticking to JavaScript wherever possible will dramatically improve your scripts' responsiveness.

How could you retrieve a range of user highlighted cells in Google Sheets using Google Apps Script?

At the moment this is the function I'm using but I have no way of testing if it will work in the spreadsheet until I publish the application.
function readSelection() {
//The commented lines aren't needed if the sheet is open already
//var sheetid = "sheet id here";
//var spreadsheet = SpreadsheetApp.openById(sheetid);
//SpreadsheetApp.setActiveSpreadsheet(spreadsheet);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
//sheet.setActiveSelection("B2:B22");
var activerange = sheet.getActiveRange();
var activecells = activerange.getValues();
return activecells;
};
I assume you mean highlighted == selected
The result depends on whether the cells are contiguous or not (non contiguous cell selection is available in the new spreadsheets features http://googleblog.blogspot.co.nz/2013/12/new-google-sheets-faster-more-powerful.html
For contiguous cells selected your code returns the values of the selection as an array, for non-contiguous cells your code will return the an array with the single value of the LAST selected item.
I suggest that this is a bug in the implementation of the new spreadsheet. If it is important to you, I suggest you raise an issue. For the old spreadsheets, you can only select contiguous cells (eg B2:B22) so it will work as you expect.
The easiest way to answer this Q is to run the code you have written! You don't have to publish anything just run the code in the script editor of the spreadsheet you are examining
and look at the log.
function readSelection() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var activerange = sheet.getActiveRange();
var activecells = activerange.getValues();
Logger.log(activecells)
return
};
There is no way to do this at the moment or to obtain the selected ranges from a script.
A request is pending and you can support it here : https://code.google.com/p/google-apps-script-issues/issues/detail?id=4056
by adding a star to the request.
If/when this function is implemented your code would look as follows:
function readSelection() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var activeranges = sheet.getSelectedRanges();
var activecells = [] ;'
for (var ar in activeranges)
activecells = activecells.concat(activeranges[ar].getValues()) ;
Logger.log(activecells)
return ;
}
note that selected ranges may overlap, so some cell contents could be added twice.