Auto sorter in Google Sheets - sorting

My program works but it takes almost a minute to update/sort an entry. How do I make it instant or at least only a couple of seconds? I have one function sort and another function that activates the sort on edit
function autoSort(e){
const row = e.range.getRow()
const column = e.range.getColumn()
if(!(column === 1 && row >=4)) return
const ss = SpreadsheetApp.getActiveSpreadsheet()
const ws = ss.getSheetByName("Week 3")
const range = ws.getRange(4,1,ws.getLastRow() -1,5)
range.sort({column: 1, ascending: false})
}
function onEdit(e){
autoSort(e)
}

Related

Filter Data and Remove Rows Fast while keeping Header

This is my final task. I am looking to filter data from column 17, that if anything in that column is less than 1, to remove the data in those rows. There is data that gets imported daily and their are a lot of rows that I want to filter fast and then delete those rows specified. I just want to keep the value of 1 in column 17. There is a frozen header at the top that does not move. What I have works but is extremely slow. Thank you for any help!
function filterData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName('CancelRawData');
var colCdata = sh1.getRange(1,17,sh1.getLastRow(),1).getValues()
var filtered = colCdata.filter(String);
Logger.log(filtered);
for(let i = colCdata.length-1;i >= 0;i--){
if(colCdata[i] <1 === true){
sh1.deleteRow(i+1)
}
}
SpreadsheetApp.getUi().alert("🎉 Congratulations, your data has been filtered!", SpreadsheetApp.getUi().ButtonSet.OK);
}
In your situation, in order to reduce the process cost of your script, how about the following modifications?
Modified script 1:
In this modification, Sheets API is used. Before you use this script, please enable Sheets API at Advanced Google services.
function filterData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName('CancelRawData');
var colCdata = sh1.getRange(1, 17, sh1.getLastRow(), 1).getValues();
var sheetId = sh1.getSheetId();
var requests = colCdata.reduce((ar, [q], i) => {
if (!isNaN(q) && q < 1) ar.push({ deleteDimension: { range: { sheetId, startIndex: i, endIndex: i + 1, dimension: "ROWS" } } });
return ar;
}, []).reverse();
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
SpreadsheetApp.getUi().alert("🎉 Congratulations, your data has been filtered!", SpreadsheetApp.getUi().ButtonSet.OK);
}
Modified script 2:
In this modification, retrieved all values are filtered, and the filtered values are put on the sheet.
function filterData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName('CancelRawData');
var range = sh1.getDataRange();
var values = range.getValues().filter(r => isNaN(r[16]) || r[16] >= 1);
range.clearContent();
sh1.getRange(1, 1, values.length, values[0].length).setValues(values);
SpreadsheetApp.getUi().alert("🎉 Congratulations, your data has been filtered!", SpreadsheetApp.getUi().ButtonSet.OK);
}
References:
Method: spreadsheets.batchUpdate
DeleteDimensionRequest

onEdit checkbox then IMPORTRANGE

I'm trying to combine a onEdit script with an import range script, I tried using "if" to check if there's a single TRUE checkbox on the selected range but the import range part does not work only the checkbox part.
I need to import only after a single TRUE checkbox is enabled on column 7 otherwise the other spreadsheet glitches.
Source for the checkbox validation script: https://stackoverflow.com/a/71798583/18249133
function onEdit(e) {
const ss = SpreadsheetApp.getActiveSheet()
const { range: { columnStart, rowStart, rowEnd }, value } = e
if (value === "FALSE") return;
const checkRowStart = 4
const checkRowEnd = 200
const checkColumn = 7
if (columnStart === checkColumn && checkRowStart <= rowStart && checkRowEnd >= rowEnd) {
for (let i = 0; i < checkRowEnd; i++) {
ss.getRange(i + checkRowStart, checkColumn).setValue(false)
}
ss.getRange(rowStart, columnStart).setValue(true)
}
}
Import range script
function import(){
importRange(
"SPREADSHEET_ID",
"SHEET NAME + RANGE",
"SPREADSHEET_ID",
"SHEET NAME + RANGE",
);
};
function importRange(sourceID, sourceRange, destinationID, destinationRangeStart) {
const sourceSS = SpreadsheetApp.openById(sourceID);
const sourceRng = sourceSS.getRange(sourceRange);
const sourceVals = sourceRng.getValues();
const destinationSS = SpreadsheetApp.openById(destinationID);
const destStartRange = destinationSS.getRange(destinationRangeStart);
const destSheet = destinationSS.getSheetByName(destStartRange.getSheet().getName());
const destRange = destSheet.getRange(
destStartRange.getRow(),
destStartRange.getColumn(),
sourceVals.length,
sourceVals[0].length
);
destRange.setValues(sourceVals);
};
Thanks in advance :]

How to make this script run in all tabs except certain tabs (Sheets)?

I'm a total newbie when it comes to scripts and I honestly don't really use it often at all but I thought it'd be fun to automatize an alphabetical order sorting I wanted to do and so I used this script:
/** Build a menu item
From https://developers.google.com/apps-script/guides/menus#menus_for_add-ons_in_google_docs_or_sheets
**/
function onOpen(e) {
var menu = SpreadsheetApp.getUi().createMenu('Sort');
if (e && e.authMode == ScriptApp.AuthMode.NONE) {
// Add a normal menu item (works in all authorization modes).
menu.addItem('Sort Sheet', 'sort');
} else {
// Add a menu item based on properties (doesn't work in AuthMode.NONE).
var properties = PropertiesService.getDocumentProperties();
var workflowStarted = properties.getProperty('workflowStarted');
if (workflowStarted) {
menu.addItem('Sort Sheet', 'sort');
} else {
menu.addItem('Sort Sheet', 'sort');
}
menu.addToUi();
}
}
function sort() {
/** Variables for customization:
Each column to sort takes two variables:
1) the column index (i.e. column A has a colum index of 1
2) Sort Asecnding -- default is to sort ascending. Set to false to sort descending
**/
//Variable for column to sort first
var sortFirst = 3; //index of column to be sorted by; 1 = column A, 2 = column B, etc.
var sortFirstAsc = true; //Set to false to sort descending
//Variables for column to sort second
var sortSecond = 1;
var sortSecondAsc = true;
//Number of header rows
var headerRows = 1;
/** End Variables for customization**/
/** Begin sorting function **/
var activeSheet = SpreadsheetApp.getActiveSheet();
var sheetName = activeSheet.getSheetName(); //name of sheet to be sorted
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var range = sheet.getRange(headerRows+1, 1, sheet.getMaxRows()-headerRows, sheet.getLastColumn());
range.sort([{column: sortFirst, ascending: sortFirstAsc}, {column: sortSecond, ascending: sortSecondAsc}]);
}
It worked very well but I wondered if there was a way to not have it work in two specific tabs of the same sheets?
I believe your goal as follows.
You want to execute the script of sort for the sheets except for the specific sheets.
In this case, how about declaring the excluded sheet names and checking the current sheet using the excluded sheet names? When this is reflected to your script, it becomes as follows.
Modified script:
In this case, please set the sheet names you want to exclude to excludeSheetNames. At sample script, when the active sheet is "Sheet1" and "Sheet2", the script below the if statement is not run.
function sort() {
var excludeSheetNames = ["Sheet1", "Sheet2"]; // <--- Added
var sortFirst = 3;
var sortFirstAsc = true;
var sortSecond = 1;
var sortSecondAsc = true;
var headerRows = 1;
var activeSheet = SpreadsheetApp.getActiveSheet();
var sheetName = activeSheet.getSheetName();
if (excludeSheetNames.includes(sheetName)) return; // <--- Added
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var range = sheet.getRange(headerRows + 1, 1, sheet.getMaxRows() - headerRows, sheet.getLastColumn());
range.sort([{ column: sortFirst, ascending: sortFirstAsc }, { column: sortSecond, ascending: sortSecondAsc }]);
}
For example, if you want to run the script below the if statement for only excludeSheetNames, please modify if (excludeSheetNames.includes(sheetName)) return; to if (!excludeSheetNames.includes(sheetName)) return;.
Reference:
includes()

Google sheets auto sort script modification to trigger only on specified column change

I have this auto sort script and it works great but I can't figure out how to change two things.
Instead of the script being triggered by every change in the entire sheet I'd like it to trigger when only two specific columns are edited (C and D).
SHEET_NAME = "North Tonawanda";
SORT_DATA_RANGE = "C:D";
SORT_ORDER = [
{column: 3, ascending: true}, // 3 = column number, sorting by descending order
{column: 4, ascending: true} // 1 = column number, sort by ascending order
];
function onEdit(e){
multiSortColumns();
}
function multiSortColumns(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(SHEET_NAME);
var range = sheet.getRange(SORT_DATA_RANGE);
range.sort(SORT_ORDER);
ss.toast('Sort Completed.');
}
Answer
I would rewrite the script as follows. This makes it visually clearer and the onEdit trigger uses the SORT_ORDER variable to see the columns that trigger the function:
Code
SHEET_NAME = "North Tonawanda";
SORT_DATA_RANGE = "C:D";
SORT_ORDER = [
{ column: 3, ascending: true },
{ column: 4, ascending: true }
];
function multiSortColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(SHEET_NAME);
var range = sheet.getRange(SORT_DATA_RANGE);
range.sort(SORT_ORDER);
ss.toast('Sort Completed.');
}
function onEdit(e) {
var editedSheet = e.range.getSheet().getName()
var columnStart = e.range.columnStart
var columnEnd = e.range.columnEnd
if (SHEET_NAME == editedSheet
&& columnStart >= SORT_ORDER[0]["column"]
&& columnEnd <= SORT_ORDER[1]["column"]) {
multiSortColumns()
}
}
References:
Simple Triggers
Event Objects
I believe your goal as follows.
From your question and your script, you want to run the function of multiSortColumns() when the columns "C" and "D" of the sheet North Tonawanda are edited.
In this case, I would like to propose to achieve your goal using the event object. When your script is modified, it becomes as follows.
Modified script:
From:
function onEdit(e){
multiSortColumns();
}
To:
function onEdit(e){
const range = e.range;
if (range.getSheet().getSheetName() != SHEET_NAME || (range.columnStart != 3 && range.columnStart != 4)) return;
multiSortColumns();
}
References:
Simple Triggers
Event Objects

Auto sorting 2 sheets based on 1 of them

I'm running a spreadsheet which contains multiple sheets, in Sheet3 I'm inputting some data and running an auto sorting code, which sorts it ascending by column D.
Sheet3 Example | Sheet1 Example
The "name" and "location" in Sheet1 are imported from Sheet3 so they swap position when Sheet3 does the sorting, however, the problem is that the info from D to F (Sheet1) isn't swapping and it will display for wrong people.
This is the script I'm using:
Modified it slightly to work for a specific sheet, since I didn't need to auto sort the whole document at the time.
/*
* #author Mike Branski (#mikebranski)
* #link https://gist.github.com/mikebranski/285b60aa5ec3da8638e5
*/
var SORT_COLUMN_INDEX = 4;
var ASCENDING = true;
var NUMBER_OF_HEADER_ROWS = 2;
var SHEET_NAME = 'Sheet3';
var activeSheet;
function autoSort(sheet) {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == SHEET_NAME) {
var range = sheet.getDataRange();
if (NUMBER_OF_HEADER_ROWS > 0) {
range = range.offset(NUMBER_OF_HEADER_ROWS, 0, (range.getNumRows() - NUMBER_OF_HEADER_ROWS));
}
range.sort( {
column: SORT_COLUMN_INDEX,
ascending: ASCENDING
} );
}
}
function onEdit(event) {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == SHEET_NAME) {
var editedCell;
activeSheet = SpreadsheetApp.getActiveSheet();
editedCell = activeSheet.getActiveCell();
if (editedCell.getColumn() == SORT_COLUMN_INDEX) {
autoSort(activeSheet);
}
}
}
function onOpen(event) {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == SHEET_NAME) {
activeSheet = SpreadsheetApp.getActiveSheet();
autoSort(activeSheet);
}
}
function onInstall(event) {
onOpen(event);
}
So basically when I edit Sheet3 and it does the auto sorting, I want the rows from D to F in Sheet1 to carry along with repositioning that comes from Sheet3. I hope I did manage to explain properly what I want.
I've tried without success to make it work; I can't figure out the proper way of doing this, especially due to the fact that Sheet1 table has different range.
I figured out how to fix the issue so I'll post the code here. Basically whenever you edit the column that you choose to sort by in Sheet3 (master sheet) it will first copy in the Sheet1 (target sheet) what changes you've made in A & B columns and then it will sort both sheets at the same time, this way the data from following columns in Sheet1 will carry along.
I used A & B columns in this example, since that's what I commented above, but can be different ranges as long as they're similar in size.
// Master Sheet Settings (Copy ranges must be similar in size)
var msName = 'Master Sheet';
var msSortCol = 4; // which column to trigger the sorting when you edit
var msSkipRows = 6; // how many rows to skip, if you have header rows
var msCopyRange = 'A7:B51'; // the range you want to copy
// Target Sheet Settings
var tsSortCol = 3;
var tsSkipRows = 10;
var tsName = 'Target Sheet';
var tsCopyRange = 'A11:B55';
var sortAscending = true;
var activeSheet;
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var editedCell = ss.getActiveRange().getColumnIndex();
if (ss.getSheetName() == msName) {
activeSheet = SpreadsheetApp.getActiveSheet();
if (editedCell == msSortCol) {
copyRow();
autoSort(activeSheet);
}
}
}
function copyRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(msName);
var values = sheet.getRange(msCopyRange).getValues();
ss.getSheetByName(tsName).getRange(tsCopyRange).setValues(values);
SpreadsheetApp.flush();
}
function autoSort() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var msheet = ss.getSheetByName(msName);
var tsheet = ss.getSheetByName(tsName);
var mrange = msheet.getDataRange();
var trange = tsheet.getDataRange();
if (ss.getSheetName() == msName) {
if (msSkipRows > 0) {
mrange = mrange.offset(msSkipRows, 0, (mrange.getNumRows() - msSkipRows));
}
if (tsSkipRows > 0) {
trange = trange.offset(tsSkipRows, 0, (trange.getNumRows() - tsSkipRows));
}
mrange.sort({ column: msSortCol, ascending: sortAscending });
trange.sort({ column: tsSortCol, ascending: sortAscending });
}
}

Resources