I would like to add a menu item to a Google Sheet that performs a task on the selected cells. A web search, including Stack Overflow, indicates that the getActiveRange() method is the proper approach. But when I query the returned Range object, regardless of what cells I have selected, it always reports the first row equal to 1 and the number of rows equal to 1. What should I be using?
Here is a stripped-down script that reproduces the issue:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
function onOpen() {
var myMenu = [];
myMenu.push({name: "Detect selection", functionName: "detectSelection"});
ss.addMenu("My Menu", myMenu);
}
function detectSelection() {
var range = sheet.getActiveRange();
var firstRow = range.getRow();
var lastRow = range.getLastRow();
var numRows = range.getNumRows();
sheet.getRange(1,1).setValue("First Row");
sheet.getRange(1,2).setValue(firstRow);
sheet.getRange(2,1).setValue("Last Row");
sheet.getRange(2,2).setValue(lastRow);
sheet.getRange(3,1).setValue("Number of Rows");
sheet.getRange(3,2).setValue(numRows);
}
I can select any group of cells I wish, choose "Detect selection" from "My Menu", and the first row, last row, and number of rows will always be reported as 1.
Try this:
Compare it to what you have and I think you can figure it out for yourself. The learning experience will be worth it. You can select any range you want.
function runOne() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getSheetByName('Sheet1');
var rg=sh.getActiveRange();
sh.clearContents();
var vA=[['rg.getRow()',rg.getRow()],['rg.getColumn()',rg.getColumn()],['rg.getLastRow()',rg.getLastRow()],['rg.getLastColumn()',rg.getLastColumn()],['rg.getNumRows()',rg.getNumRows()],['rg.getNumColumns()',rg.getNumColumns()]];
var vB=[];
for(var i=0;i<rg.getHeight();i++) {
vB[i]=[];
for(var j=0;j<rg.getWidth();j++) {
vB[i][j]=Utilities.formatString('%s,%s',rg.getRow()+i,rg.getColumn()+j);
}
}
sh.getRange(rg.getRow(),rg.getColumn(),vB.length,vB[0].length).setValues(vB);
sh.getRange(sh.getLastRow()+1,rg.getColumn(),vA.length,vA[0].length).setValues(vA);
}
Related
I'm automating the appointment process at my company and I have been successfully implementing some google scripts with time trigger.
Whenever someone takes an appointment on Calendly it creates at row with several information through Zapier.
I then have a script with several functions that operates on the newly added rows.
One function auto sort the new row based on the date column then two others functions fill two columns with a checkbox(FALSE) and a datavalidation based on a list of choice. All of those functions are time triggered, let's say 30 minutes.
The problem is whenever the trigger happens it automatically checks the checkbox to TRUE for the entire column and to the first choice of the list for the entire datavalidation column.
How can I solve that ?
var SORT_COLUMN_INDEX = 4;
var ASCENDING = true;
var NUMBER_OF_HEADER_ROWS = 1;
var activeSheet;
function autoSort() {
console.log(sheet, activeSheet)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getDataRange();
if (NUMBER_OF_HEADER_ROWS > 0) {
range = range.offset(NUMBER_OF_HEADER_ROWS, 0);
}
range.sort( {
column: SORT_COLUMN_INDEX,
ascending: ASCENDING
} );
}
// Fonction to automatically add data validation in column K
function setDataValidationComing() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var listOfChoices = ["Coming","Not Coming","Message Left", "Unreachable"]
var validation = SpreadsheetApp.newDataValidation().requireValueInList(listOfChoices).build();;
sheet.getRange("K2").setDataValidation(validation);
var lr = sheet.getLastRow();
var fillDownRange = sheet.getRange(2, 11, lr-1);
sheet.getRange("K2").copyTo(fillDownRange);
}
// Fonction to automatically add checkbox for appointment honored in column L
function setCheckboxCame() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var validation = SpreadsheetApp.newDataValidation().requireCheckbox().build();
sheet.getRange("L2").setDataValidation(validation);
var lr = sheet.getLastRow();
var fillDownRange = sheet.getRange(2, 12, lr-1);
sheet.getRange("L2").copyTo(fillDownRange);
}
Here is a screenshot of the google sheet. Google sheet screenshot
Thanks for your help, I've just started using Google Script a week ago !
The problem is whenever the trigger happens it automatically checks the checkbox to TRUE for the entire column and to the first choice of the list for the entire datavalidation column.
It is not actually checking it to true for the entire column. It is copying the value of the first row after the header, and applying that value to each checkbox in the column. The first row of data shows 'Coming' so if you run that script, it will apply Coming to all of them. If you change it to 'Not Coming', it would apply 'Not coming' to every row. This is because of this line:
sheet.getRange("K2").copyTo(fillDownRange);
You don't want to copy the value of K2, you want to only copy the validation. So that line should really be:
`sheet.getRange("K2").copyTo(fillDownRange, SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION, false);`
As for the checkbox, that's a little bit trickier, but the same concept applies:
function setCheckboxCame() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// UPDATED THIS PART
var validation = SpreadsheetApp.newDataValidation().requireCheckbox();
validation.setAllowInvalid(false);
validation.build();
var lr = sheet.getLastRow();
var fillDownRange = sheet.getRange(2, 12, lr-1);
// CHANGE THIS:
//sheet.getRange("L2").copyTo(fillDownRange);
// TO THIS:
fillDownRange.setDataValidation(validation);
}
I'm trying to automate a sheet to sort rows based on date, and transfer rows when a checkbox is selected. I got the transfer part figured out, but the sort function only works on the tabs I'm getting rid of, and not the tabs I'm keeping, and I don't understand why.
Here's a couple sort functions that both work, but not on the tabs I need them to work on:
function onEdit(event) {
// assumes source data in sheet named Active
// target sheet of move to named Completed
// getColumn with check-boxes is currently set to colu 8 or H
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
//sort Active by oldest date at the top, using column D or 4
var sheet = event.source.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 4;
var tableRange = "A2:T99"; // What to sort.
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}
}
And:
function myFunction()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Active");
var rows = sheet.getLastRow();
var columns = sheet.getLastColumn();
var sortedValues = sheet.getRange(2, 1, rows-1, columns).getValues().sort();
sheet.getRange(2, 1, rows-1, columns).setValues(sortedValues);
}
The sort works on the tabs with names on them, but not the tabs called Active and Completed. With the automation, I'm getting rid of the named tabs and we'll just have Active and Complete.
I've removed the transfer upon complete function as it worked just fine, and I'm just focusing on the sort, since it seems to be the issue.
Any suggestions for why the two tabs aren't sorting?
Here's a copy of the spreadsheet I'm working on:
https://docs.google.com/spreadsheets/d/17VeCzfqAclxUN-kVIrWZSII-7clsNfttmVVU8s0JhFs/edit?usp=sharing
EDIT: solved by Cooper below.
Final version of code that worked for my needs:
function onEdit(event) {
var sh=SpreadsheetApp.getActive().getSheetByName("Active");
sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).sort({column:4,ascending:true});
}
Try this for sorting by column 4:
function myFunction() {
var sh=SpreadsheetApp.getActive().getSheetByName("Active");
sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).sort({column:4,ascending;true});
}
Sorting
I am new to this and I am having some trouble trying to figure this out. I have a spreadsheet that collects data from a google form. I am trying to find a way to move that data based on a column answer to a different google sheet. (Not a sheet in the same document but a different document all together). It seems like there is some information about moving to a different tab in the same document, but not a different document.
I will first say that I tried just an IMPORTRANGE function, but that only mirrors the data, and does not let you update or change cells.
Here is what I have been able to piece together so far, but I may be way off.
I have a trigger that would run every hour.
function myFunction() {
var ss = SpreadsheetApp.openById('1U0I9SkbGkHgm-vRkwf2Ppc_yxlqrVlg2t8yKRy3sYuI');
var sheetOrg = ss.getSheetByName("Form Responses 1");
var value1ToWatch = "ANDERSON";
var value2ToWatch = "BARNES";
var sheetNameToMoveTheRowTo = "Sheet1"; //sheet has same name for each target openByID(" *url key* ")
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ts1 =SpreadsheetApp.openById("1PxV1PQrMdu_2aSru4dpan8cUgHYdlYPUp5anyzjMAms");
sheet1in = ts1.getSheetByName("Sheet1");
var ts2 = SpreadsheetApp.openById("1BYyQZNiXc2QqsyqWs7uazjl5B6mfFYM1xj3u8gWyYOQ");
sheet1in = ts2.getSheetByName("Sheet1");
arr = [],
values = sheetOrg.getDataRange().getValues(),
i = values.length;
while (--i) {
if (value1ToWatch.indexOf(values[i][1]) > -1) {
arr.unshift(values[i])
sheetOrg.deleteRow(i + 1)
sheet1in.getRange(sheet1in.getLastRow()+1, 1, arr.length,
arr[0].length).setValues(arr);
};
if (value2ToWatch.indexOf(values[i][1]) > -1) {
arr.unshift(values[i])
sheetOrg.deleteRow(i + 1)
sheet2in.getRange(sheet2in.getLastRow()+1, 1, arr.length,
arr[0].length).setValues(arr);
};
}
}
The sheet google forms dumps the information into is "Form Responses 1".
Column B is the cells I want to get the values from. (There are a total of 9 different values that it can be like "ANDERSON", "BARNES", "SMITH", etc).
For sheetNameToMoveTheRowTo is "Sheet1" - that may be confusing and I may need to change that, but for example
ts1 =SpreadsheetApp.openById("1PxV1PQrMdu_2aSru4dpan8cUgHYdlYPUp5anyzjMAms") the sheet name that I want the information moved to is "Sheet1".
ts2 = SpreadsheetApp.openById("1BYyQZNiXc2QqsyqWs7uazjl5B6mfFYM1xj3u8gWyYOQ") the sheet name is also "Sheet1" but in a different document.
I think if I were able to get the "ANDERSON" one to work, then I can just add additional variables for each possible response, and just copy and paste additional "IF" statements, just changing the valueToWatch and targetSheet values. <= if that is not correct please let me know
I have tried to both debug, and run the script above but nothing happens. There are no errors reported on the debug, but it is not moving any information over.
Any idea what I am doing wrong?
// UPDATE I got this to work. I have updated the code listed with what worked for me.
I think that copyTo() method will not work like you mentioned, it operates on same SpreadSheet. I'm sending you example with looping on source sheet data and then setting the target sheet values with it.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var allrange = ss.getActiveRange();
var range = sheet.getRange("A1:A10");
var values = range.getValues();
var allvals = [];
for(var i = 0; i < values.length; i++) {
allvals.push( values[i] ) ;
}
var dataLength = allvals.length;
// alert data
var ui = SpreadsheetApp.getUi();
// ui.alert( JSON.stringify(allvals) )
// copy to new Google SpreadSheet
var newSheet = SpreadsheetApp.openById('1F79XkNPWm2cCWlB2JP3K4tAYRESKUgtHK4Pn2vbJEiI').getSheets()[0];
var tmp = "A1:A" + dataLength ;
var newRange = newSheet.getRange( tmp );
newRange.setValues( allvals );
}
Here's a simple example of moving data from one spreadsheet to another.
function movingDataToSS(){
var ss=SpreadsheetApp.getActive();
var dss=SpreadsheetApp.openById('ssId');
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var vA=rg.getValues();
var dsh=dss.getSheetByName('Sheet1');
var drg=dsh.getRange(1,1,rg.getHeight(),rg.getWidth()).setValues(vA);
}
If you interested in placing some conditions on your output by only getting output from odd rows and even columns.
function movingDataOddRowsAndEvenCols(){
var ss=SpreadsheetApp.getActive();
var dss=SpreadsheetApp.openById('ssId');
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var vA=rg.getValues();
var h=rg.getHeight();
var w=rg.getWidth();
var dsh=dss.getSheetByName('Sheet1');
for(var i=0;i<h;i++){
var out=[];
if(i%2==0){
for(var j=0;j<w;j++){
if(j%2==1){
out.push(vA[i][j]);
}
}
dsh.appendRow(out);
}
}
}
I have a google doc that has formulas in column C and Column D. It's a forecasting sheet so I know what we have on hand (C) and what we have on order (D). I want a script that opens a menu at the top so I can quickly hide or unhide Column C values that are equal to 0 and the same with column D.
I've pulled this script from someone on here to create a menu for hiding rows but I can't get it to work with my application. I'm not sure if the formulas in the cells prevent the function from finding 0's or not.
UPDATE: I have the first function working. It hides all rows with a 0 in column C. When a row is hidden and is updated to have a value, the function does not unhide it though. The second function is still not unhiding all. Once I get these two figured out I can build out the same thing for Column D. Below is updated script
function onOpen() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// create menu
var menu = [{name: "Show Only On Hand", functionName: "hideRow"},
{name: "Show All", functionName: "showRow"}];
// add to menu
ss.addMenu("Filter", menu);
}
function hideRow() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get first sheet
var sheet = ss.getSheetByName('Inventory')
// get data
var data = sheet.getDataRange();
// get number of rows
var lastRow = data.getLastRow()+1;
Logger.log(lastRow);
// itterate through rows
for(var i=1; i<lastRow; i++) {
if(data.getCell(i, 3).getValue() == 0) {
sheet.hideRows(i);
}
}
}
function showRow() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get first sheet
var sheet = ss.getSheets()[0];
// get data
var data = sheet.getDataRange();
// get number of rows
var lastRow = data.getLastRow();
// show all rows
sheet.showRows(lastRow, 1);
}
I won't do everything for you, cause this isn't this sites goal, but I'll point to where the script could be failing, by line:
var sheet = ss.getSheets()[0];
Is it always the first sheet you're using? If not, you can use the method getSheetByName().
for(var i=1; i<lastCol; i++) {
This iterates trough the columns, do you really want that? If it is for rows, you'll need to change the getLastColumn for a row method.
if(data.getCell(i, 3).getValue() == '0') {
Again, if it is row that you want to hide, you'll have to refactor getCell, since i is the row, and 3 is the column, you can add another conditional since you also want column D.
Also, you may need to log the data.getCell() to check if the 0 is really coming as a String, if it is coming as a number remove the single quotes.
sheet.hideColumns(i);
Again, for row, this need to be refactored.
Update:
To show all you can:
var sheet = SpreadsheetApp.getActiveSheet();
var fullSheetRange = sheet.getRange(1,1,sheet.getMaxRows(), sheet.getMaxColumns() )
sheet.unhideColumn( fullSheetRange );
sheet.unhideRow( fullSheetRange ) ;
And then hide them. To do this for column all you have to do is copy and change the column C specied in getCell(), you can add another condition in the if with the OR logical operator.
Huge thanks to Kriggs for helping me figure this one out. I'm sure it's a bit clunky, but it works perfect for what I need.
function onOpen() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// create menu
var menu = [{name: "Show Only On Hand", functionName: "hideRow"},
{name: "Show Only On Order", functionName: "hideorder"}];
// add to menu
ss.addMenu("Filter", menu);
}
function hideRow() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get first sheet
var sheet = ss.getSheetByName('Inventory')
// get data
var data = sheet.getDataRange();
// get number of rows
var lastRow = data.getLastRow()+1;
Logger.log(lastRow);
var sheet = SpreadsheetApp.getActiveSheet();
var fullSheetRange = sheet.getRange(1,1,sheet.getMaxRows(), sheet.getMaxColumns() )
sheet.unhideColumn( fullSheetRange );
sheet.unhideRow( fullSheetRange ) ;
// itterate through rows
for(var i=1; i<lastRow; i++) {
if(data.getCell(i, 3).getValue() == 0) {
sheet.hideRows(i);
}
}
}
function hideorder() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get first sheet
var sheet = ss.getSheetByName('Inventory');
// get data
var data = sheet.getDataRange();
// get number of rows
var lastRow = data.getLastRow()+1;
Logger.log(lastRow);
var sheet = SpreadsheetApp.getActiveSheet();
var fullSheetRange = sheet.getRange(1,1,sheet.getMaxRows(), sheet.getMaxColumns() )
sheet.unhideColumn( fullSheetRange );
sheet.unhideRow( fullSheetRange ) ;
// itterate through rows
for(var i=1; i<lastRow; i++) {
if(data.getCell(i, 4).getValue() == 0) {
sheet.hideRows(i);
}
}
}
I want to focus the last row on a specific sheet (or if possible on every one of them) so here is my function triggered by onOpen on the sheet:
function FocusLastRows() {
var spsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spsheet.getSheetByName("Usuelle_2013");
var rowInt = sheet.getLastRow();
var colInt = sheet.getLastColumn();
var range = sheet.getRange(rowInt, colInt)
sheet.setActiveRange(range)
}
This event works but if my first loaded sheet is different from the one I want it to focus on it just teleports to the other sheet. Best will be to set an .onOpen on the spreadsheet.
Is that possible?
I found a good alternative for it. It's custom menu with function link to it.
//Pierre-luc Des. Script
//Build your menu
function setMenu() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menubuttons = [ {name: "Dernière ligne usuelle", functionName: "GOTOLastRow}];
ss.addMenu("Outils", menubuttons);
}
function GOTOLastRow() {
var spsheet = SpreadsheetApp.getActiveSpreadsheet();
var currentTime = new Date();
var sheet = spsheet.getSheetByName("Usuelle_" + currentTime.getFullYear());
var rowInt = sheet.getLastRow();
var colInt = sheet.getLastColumn();
var range = sheet.getRange(rowInt, colInt);
sheet.setActiveRange(range);
}
Now i will have a clickable menu "Outils" which will let me do the function.
This is rather inelegant, but perhaps try just checking whether the activated sheet is the target sheet? Something like this:
function FocusLastRows() {
var spsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spsheet.getSheetByName("Usuelle_2013");
if (sheet == spsheet) {
var rowInt = sheet.getLastRow();
var colInt = sheet.getLastColumn();
var range = sheet.getRange(rowInt, colInt);
sheet.setActiveRange(range);
}
}
The Google Apps Script documentation seems to indicate that you'll need custom libraries to get any other trigger events than the ones provided, and I'm not sure if it's even in-principle possible to trigger only when a certain specific sheet is activated.
I figure checking the sheet with if comparisons is the next-simplest solution.