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
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 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);
}
Ive been working on automatically sorting my data (ascending based on 2nd row 1st column data) and I found some tips through searching online but encountered an error which seems I cant find an answer through the net.
so heres the scenario:
I have 2 sheets, Sheet1 & Sheet2, the data from sheet1 is linked through sheet2 although sheet2 has additional columns,
this is sheet1
and this is sheet2
notice that the Column lastname and code in both sheets are thesame, the difference is the column Gender (Formatted on drop-down list) & Bdate(cell formatted as date)
I found a script that seems to work but I does not properly work completely, here is the output after I run the script.
notice the columns that inside the red box, it seems gender and bdate didnt follow the auto sort.
here is my code:
function autosortOnEdit() {
var sheetNames = ["Sheet1", "Sheet2"];
var ss = SpreadsheetApp.getActiveSpreadsheet();
sheetNames.forEach(function(name) {
var sheet = ss.getSheetByName(name);
var range = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn() -1);
range.sort({column: 1, ascending: true});
});
}
my observation is I think this script does not work on cells that are formatted like the example above.
I want to sort this automatically based on column A "last name".
how can i make this script work even on formatted cells?
Thanks in Advance, I will continue searching through the net.
Not sure how to use range.sort({column: 1, ascending: true}); or how does it work, but whenever I want to sort sheet values, I do the following:
function myFunction()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
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);
}
Try this instead of your code, hope this helps as it is successfully sorting all the values when I tested.
EDIT
To apply the same to all sheets inside a spreadsheet, you can get sheet names and iterate it in for loop one by one. Check the code:
function myFunction()
{
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var currentSheetName, currentSheet;
for(var i=0; i<sheets.length; i++)
{
currentSheetName = sheets[i].getName();
currentSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(currentSheetName);
var rows = currentSheet.getLastRow();
var columns = currentSheet.getLastColumn();
var sortedValues = currentSheet.getRange(2, 1, rows-1, columns).getValues().sort();
currentSheet.getRange(2, 1, rows-1, columns).setValues(sortedValues);
}
}
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 have a google spreadsheet with two sheets called Robin and Lucy.
I've made/found/mangled a script to sort the data on the sheet each time I add some data to column A
function onEdit(event){
var sheet = event.source.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 1;
var tableRange = "a2:I30";
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy } );
}
}
This script works great, but I only want it it be applied on the first sheet, Robin. The data in the second sheet, Lucy, isn't the same so I'm going to create another script for a different range for that one, once I get my head around this problem.
I think I need to use the getSheetByName("Robin") but I can't seem to get it to work.
Thanks in advance
You can put your whole function in a condition like this :
function onEdit(event){
var sheet = event.source.getActiveSheet();
if(sheet.getName()=='Robin'){
var editedCell = sheet.getActiveCell();
var columnToSortBy = 1;
var tableRange = "a2:I30";
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy } );
}
}
}
or you could also return if the condition is false like below
...
var sheet = event.source.getActiveSheet();
if(sheet.getName()!='Robin'){return}
...
Both methods will work the same way.
Forward the event object received by onEdit to the specific function which handles events on the desired sheet.
function onEdit(event) {
var name = event.range.getSheet().getName();
switch (name) {
case "Robin":
updateRobin(event);
break;
case "Lucy":
updateLucy(event);
break;
}
}
function updateLucy(event) {
// Code designed to handle edits to the "Lucy" sheet.
}
function updateRobin(event) {
// Code designed to handle edits to the "Robin" sheet.
}
I would also suggest replacing this line:
var tableRange = "a2:I30";
with this:
var tableRange = sheet.getDataRange();
As your range grows, the range returned by getDataRange() will adjust to accommodate it. No need to use hard-coded address, makes the code more flexible
FYI:
I've found some useful examples here:
http://www.javascript-spreadsheet-programming.com