Google sheets, get range of rows, where column intersect cell is empty - sorting

Basically I want to use an auto-sorting script through the script editor in Google sheets, but I want it to smartly pick a range of rows, by only selecting those rows where a specific column has an empty cell.
All other rows that have that column's cell populated would stay put. Here is the sort code I've found.
/**
* Automatically sorts the 1st column (not the header row) Ascending.
*/
function onEdit(event){
var sheet = event.source.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 1;
var tableRange = "A2:T99"; // What to sort.
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}
}
Thanks.

Note: this will only sort the values in the cells (metadata such as formats, notes etc will not be sorted, as they would with the GAS range.sort() method).
function onEdit(event)
{
var sheet = event.source.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 0; // zero-based index
if (editedCell.getColumn() == columnToSortBy)
{
var columnToWatch = 5; // checking this column for blank cells (zero-based index)
var tableRange = "A2:T99"; // what to sort
var range = sheet.getRange(tableRange);
var data = range.getValues();
var dataCopy = data.slice();
dataCopy.sort(function(a, b) {return a[columnToSortBy] > b[columnToSortBy] ? 1 : (a[columnToSortBy] < b[columnToSortBy] ? -1 : 0);});
var j = -1;
for (var i = 0, length = data.length; i < length; i++)
{
if (data[i][columnToWatch].toString().length)
{
do j++; while (!dataCopy[j][columnToWatch].toString().length);
data[i] = dataCopy[j];
}
}
range.setValues(data);
}
}

Related

Filter & Delete rows of data based off of column value fast! (Google Sheets)

Is there a way to filter the data in column Q off my google sheet faster then reading line one by one. There is daily about 400+ lines it needs to scan through and I need to delete every row of data if the data in column Q is less than 1 right now watching it, it takes about 10+ minutes.
function UpdateLog() {
var returnSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('CancelRawData');
var rowCount = returnSheet.getLastRow();
for (i = rowCount; i > 0; i--) {
var rrCell = 'Q' + i;
var cell = returnSheet.getRange(rrCell).getValue();
if (cell < 1 ){
returnSheet.deleteRow(i);
}
}
{
SpreadsheetApp.getUi().alert("🎉 Congratulations, your data has been updated", SpreadsheetApp.getUi().ButtonSet.OK);
}
}
Try it this way:
function UpdateLog() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const vs = sh.getDataRange().getValues();
let d = 0;
vs.forEach((r, i) => {
if (!isNaN(r[16]) && r[16] < 1){
sh.deleteRow(i + 1 - d++);
}
});
}
This is a bit quicker
function UpdateLog() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const vs = sh.getDataRange().getValues().filter(r => !isNaN(r[16]) && r[16] < 1);
sh.clearContents();
sh.getRange(1,1,vs.length,vs[0].length).setValues(vs);
}

Enhancing Performance in Student Info App

I've built a simple Student Information platform using Google Sheets. It allows the user to query, update and create new student info on a user interface. Please refer to this sheet to see how it works.
The functions to Refresh/Update/Save are inside the Actions button on a menu bar. Everything seems to work well however when the number of records increases, say above 100 records, all the functions slow down and it gets extremely slow with 200+ records.
Appreciate if anyone could help to take a look at the scripts as I suspect they need to be optimized.
Many thanks in advance!
function UpdateDataIntoMaster() { //This script is used in the SAVE button in UPDATE sheet)
/*Get data from UPDATE Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheet = ss.getSheetByName('UPDATE');
var range = sheet.getRange("D30:AE30"); //All data transposed into this line. MUST be updated if more fields are added into the Data sheet
var values = range.getValues();
var rangeForKey = sheet.getRange("D30") //Student Name is used as the
key identifier
var keyValue = rangeForKey.getValue();
/*Pass in keyValue(identifier = Student Name)
and all data in the function below in order
to update master data sheet*/
updDbase(keyValue,values);
function updDbase(keyValue,values) {
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY")
var sheet = ss.getSheetByName('Data');
var data = sheet.getDataRange().getValues();
var noOfRow = values.length
var noOfCol = values[0].length
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = ss.getSheetByName("Data").getRange(i+1,1).getValue(); //Get the Student Name from Data sheet
if (keyData == keyValue) {
// for (var j=0; j < data[i].length; j++) { // this is going through all the cell of a row
var row = Number(i)+1;
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Update Information","Are you sure you want to update the student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES)
{
var sheets = ss.getSheetByName("Data").getRange(row,1,noOfRow,noOfCol).setValues(values);
}//If response == YES
}
}
}
}
function CreateNew() {
/*Get data from Inquiry Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheetNew = ss.getSheetByName('Create New');
var range = sheetNew.getRange("D30:AZE30"); //All data transposed into
this line
var values = range.getValues();
var rangeForKey = sheetNew.getRange("E30") //Using Student ID as key identifier
var keyValue = rangeForKey.getValue();
var noOfRow = values.length
var noOfCol = values[0].length
var sheetData = ss.getSheetByName('Data');
var lastRow = sheetData.getLastRow();
var data = sheetData.getDataRange().getValues();
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = sheetData.getRange(i+1,2).getValue(); //Get the Student ID from Data sheet
if (keyData == keyValue) {
AlertBox();//If Student ID is found, to prompt Student ID already
exist
return;
} //If
} //For
/*Confirming with user whether to proceed to create new entry*/
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Create New Record","Are you sure you want to
create new student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES){
if (keyValue == ""){
var response = sh.alert("Create New Record","Unable to proceed
because Student ID is empty", sh.ButtonSet.OK);
return;}
else {
//var response = sh.alert("Create New Record","Unable to
proceed because Student ID is empty", sh.ButtonSet.OK);
var sheets =
sheetData.getRange(lastRow+1,1,1,noOfCol).setValues(values)
}
}//If
}
function EditStudentInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Inquiry");
//var ss = SpreadsheetApp.getActive();
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
function EditContent() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Update");
var rangeContentCol1 = s.getRange("E3:E23");
var CopyContentCol1 = s.getRange("E3:E23").getValues();
var rangeContentCol2 = s.getRange("I3:I23");
var CopyContentCol2 = s.getRange("I3:I23").getValues();
rangeContentCol1.clearContent();
rangeContentCol2.clearContent();
var PasteContentCol1 =
s.getRange("E3:E23").setValues(CopyContentCol1);
var PasteContentCol2 = s.getRange("I3:I23").setValues(CopyContentCol2);
}
A common performance mistake people with Apps Script is doing the .getRange().getValues() within their for loops. Performance-wise these get and set calls are quite expensive.
Lucky the fix for this is quite easy - get all the data at once first, then loops through it. You actually do this already, sort of. In your script you get the whole data range, but then only use a part of the data and instead do another getValues call. I've updated the two areas in your script that had getRange() calls in a for loop --> var keyData = data[i][0];
function UpdateDataIntoMaster() { //This script is used in the SAVE button in UPDATE sheet)
/*Get data from UPDATE Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheet = ss.getSheetByName('UPDATE');
var range = sheet.getRange("D30:AE30"); //All data transposed into this line. MUST be updated if more fields are added into the Data sheet
var values = range.getValues();
var rangeForKey = sheet.getRange("D30") //Student Name is used as the
key identifier
var keyValue = rangeForKey.getValue();
/*Pass in keyValue(identifier = Student Name)
and all data in the function below in order
to update master data sheet*/
updDbase(keyValue,values);
function updDbase(keyValue,values) {
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY")
var sheet = ss.getSheetByName('Data');
var data = sheet.getDataRange().getValues();
var noOfRow = values.length
var noOfCol = values[0].length
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = data[i][0]; //Use the data that is already loaded.
if (keyData == keyValue) {
// for (var j=0; j < data[i].length; j++) { // this is going through all the cell of a row
var row = Number(i)+1;
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Update Information","Are you sure you want to update the student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES)
{
var sheets = ss.getSheetByName("Data").getRange(row,1,noOfRow,noOfCol).setValues(values);
}//If response == YES
}
}
}
}
function CreateNew() {
/*Get data from Inquiry Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheetNew = ss.getSheetByName('Create New');
var range = sheetNew.getRange("D30:AZE30"); //All data transposed into
this line
var values = range.getValues();
var rangeForKey = sheetNew.getRange("E30") //Using Student ID as key identifier
var keyValue = rangeForKey.getValue();
var noOfRow = values.length
var noOfCol = values[0].length
var sheetData = ss.getSheetByName('Data');
var lastRow = sheetData.getLastRow();
var data = sheetData.getDataRange().getValues();
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = data[i][0]; //Use the data that is already loaded.
if (keyData == keyValue) {
AlertBox();//If Student ID is found, to prompt Student ID already
exist
return;
} //If
} //For
/*Confirming with user whether to proceed to create new entry*/
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Create New Record","Are you sure you want to
create new student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES){
if (keyValue == ""){
var response = sh.alert("Create New Record","Unable to proceed
because Student ID is empty", sh.ButtonSet.OK);
return;}
else {
//var response = sh.alert("Create New Record","Unable to
proceed because Student ID is empty", sh.ButtonSet.OK);
var sheets =
sheetData.getRange(lastRow+1,1,1,noOfCol).setValues(values)
}
}//If
}
function EditStudentInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Inquiry");
//var ss = SpreadsheetApp.getActive();
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
function EditContent() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Update");
var rangeContentCol1 = s.getRange("E3:E23");
var CopyContentCol1 = s.getRange("E3:E23").getValues();
var rangeContentCol2 = s.getRange("I3:I23");
var CopyContentCol2 = s.getRange("I3:I23").getValues();
rangeContentCol1.clearContent();
rangeContentCol2.clearContent();
var PasteContentCol1 =
s.getRange("E3:E23").setValues(CopyContentCol1);
var PasteContentCol2 = s.getRange("I3:I23").setValues(CopyContentCol2);
}
Give this a test and let me know if it helps!

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 });
}
}

Auto Sort not working on Multiple Sheets within one Google Sheet

I want Sheet 2 (Titled "Deals in Escrow") to auto sort by date and Sheet 1 (Titled "Loan Inquiries") to auto sort by last name. Both Sheets are within a single Google Sheet. I have found the following script, and I modified it slightly and it works great on Sheet 2 to auto sort by date;
//Updates sort for range automatically
function onEdit(event){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 7;
var tableRange = "A3:T99"; //range to be sorted
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}
}
However, I want the 1st Sheet to auto sort by Last Name which is in Column 2, rather than Column 7 that the script refers to.
For what it's worth, I made the following changes to the script and added in the following script which works well to sort by last name in column 2 on Sheet 1;
//Updates sort for range automatically
function onEdit(event){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 2;
var tableRange = "A3:T99"; //range to be sorted
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}
}
But the problem is that by doing so, the other script to auto sort by date in Sheet 2 has now been disabled.
I've also tried creating a Name Range in each sheet and putting NamedRange1 and NameRange2 in the table range section of the script. It hasn't changed anything. Sheet 1 is still sorting by last name and Sheet 2 is not sorting.
//Updates sort for range automatically
function onEdit(event){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var editedCell = sheet.getActiveCell();
var columnToSortBy = 2;
var tableRange = "NamedRange1"; //range to be sorted
if(editedCell.getColumn() == columnToSortBy){
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}
}
Try:
function onEdit(event){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = event.source.getActiveSheet().getName()
var editedCell = event.range.getSheet().getActiveCell();
if(sheet=="Loan Inquiries"){
var columnToSortBy = 2;
var tableRange = "A3:T99"; //range to be sorted
if(editedCell.getColumn() == columnToSortBy){
var range = ss.getActiveSheet().getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}}
else if(sheet=="Deals in Escrow"){
var columnToSortBy = 7;
var tableRange = "A3:T99"; //range to be sorted
if(editedCell.getColumn() == columnToSortBy){
var range = ss.getActiveSheet().getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}
else{return}
}}
Test spreadsheet:
https://docs.google.com/spreadsheets/d/13vveA0n4w84yQ71no7LgGonz_wLE7H_RA1ocDOtLZPQ/edit?usp=sharing
I would recommend using the getSheetByName method instead :
for example:
var ss = SpreadsheetApp.getActiveSheet().getSheetByName('Deals in Escrow');

How to set i to 0 of the first item from a json criteria

I want to set i to 0 of the first item from a json criteria, eg. if the criteria is green in this case the i will start from 3... if criteria = blue it will start on 2... i need to set it to start from 0 or 1 whether it is.. also how to count total of a criteria, eg. green total is 2,, blue=1, red=2... thanks in advance!
var myBox_html ="";
var i = 0;
function createDiv(1x,2x,3x) {
A = '<something>'+1x;
B = '<something>'+2x;
C = '<something>'+3x;
myBox_html += '<something-more>'+A+B+C;
}
criteria // is a parameter from url, in this case means green
get_it = function(doc) {
var jsonData = eval('(' + doc + ')');
for (var i=0; i<jsonvar.name.length; i++) {
var 1x = jsonvar.name[i].1;
var 2x = jsonvar.name[i].2;
var 3x = jsonvar.name[i].3;
if (1x == criteria){
var Div = createDiv(1x,2x,3x);
} else {null}
}
document.getElementById("myBox").innerHTML = myBox_html;
}
get_it();
json should look like this:
var jsonvar = {"name":[{"1":"red","2":"round","3":"fruit"},{"1":"red","2":"squared","3":"box"},{"1":"blue","2":"squared","3":"box"},{"1":"green","2":"squared","3":"box"},{"1":"green","2":"pear","3":"fruit"}]};
Consider several solutions:
1: Generate criteria-grouped JSON response on the server-side. E.g.
var jsonvar = '{"name":{
"red": [{"1":"red","2":"round","3":"fruit"}, {"1":"red","2":"squared","3":"box"}],
"blue": [{"1":"blue","2":"squared","3":"box"}],
"green":[{"1":"green","2":"squared","3":"box"}, {"1":"green","2":"pear","3":"fruit"}]}}';
2: Convert you JSON array to criteria-grouped format as defined above. Here is sample routine for such a grouping:
function group_elements (arr) {
var result = {};
for (var i=0; i < arr.length; i++) {
if (!result[arr[i][1]]) {
result[arr[i][1]] = [];
}
result[arr[i][1]].push(arr[i]);
}
return result;
}
Both solutions allows you to iterate only filtered records and count length of group.

Resources