How to delete cells if no match was found? - google-apps-script

I'm having difficulties writing the IF statement to satisfy my condition. I have 2 sheets: Main and Logistics. The first one has specific information about cargo and it's transportation, such as trailer, position and arrival date. The second sheet contains all of the transportation information, such names of the trailer, drivers, arrivals, departures etc. Based on the scheduled trailers on sheet "Logistics", the user can specify which of the available trailers he wants to use for the cargo in question.
However, in a situation when the trailer gets deleted from "Logistics" due to a cancellation, I am unable to revoke previously made selection on "Main". My idea is to make this script look for matching combinations of Destination and Trailer on both sheets (columns 8 and 13 on Main, columns 1 and 2 on Logistics). If there is a row on "Main" for which no matching trailer with the same destination was found on "Logistics", the script should set columns 13, 14 and 16 on "Main" to empty.
Could someone help me write an IF statement to satisfy this condition?
I have attached the bit of code I have so far. I think only IF statement needs modifying. Thanks for the help!
function deleteSelection() {
// Main variables:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetMain = ss.getSheetByName("Main");
var tabMain = sheetMain.getRange(2, 1, sheetMain.getLastRow(), 18);
var dataMain = tabMain.getValues();
// Logistics variables:
var sheet = ss.getSheetByName("Logistics");
var dataRange = sheet.getRange(2, 1, sheet.getLastRow(), 9);
var data = dataRange.getValues();
for(var i = 0; i < dataMain.length; i++){
for(var j = 0; j < data.length; j++){
// Compare data: if there is no match between 2 sheets, set "Trailer", "Position" and "Arrival date" to empty:
if(dataMain[i][7].toLowerCase() == data[j][0].toLowerCase() && dataMain[i][12] == data[j][1]){
} else{
dataMain[i][12] = "";
dataMain[i][13] = "";
dataMain[i][15] = "";
}
}
}
// Take the modified tab and put it on the spreadsheet
tabMain.setValues(dataMain);
}
UPD: Added a sample link. On "Logistics" you can see crossed out row, upon delete of which, a script should delete crossed out rows on "Main".

You are trying to establish whether the Event and Trailer values on Main match a value on Logistics.
I have taken a slightly different approach to you. I concatenated the value of the Main "EVent" and "Trailer" and used that value to find the match on Logistics.
If a Match is found, then the script can break out of that loop and proceed to the next loop.
If no Match is found, then a variable is incremented (since the match may be found in a subsequent comparison). However, once all the values on Logistics have been evaluated, if the number of mismatches is equal to the number of records on Logistics, then the values on Main must be updated.
function so5992862301() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetMain = ss.getSheetByName("Main");
var tabMain = sheetMain.getRange(2, 1, sheetMain.getLastRow()-1, 18);
var dataMain = tabMain.getValues();
// Logger.log("DEBUG: Last row = "+sheetMain.getLastRow()+", length of dataMain = "+dataMain.length+" tab main = "+tabMain.getA1Notation());
// Logistics variables:
var sheet = ss.getSheetByName("Logistics");
var dataRange = sheet.getRange(2, 1, sheet.getLastRow()-1, 9);
var data = dataRange.getValues();
// Logger.log("DEBUG: Logistics Last row = "+sheet.getLastRow()+", length of data = "+data.length+" dataRange = "+dataRange.getA1Notation());
// start loop through Main
for(var i = 0; i < dataMain.length; i++){
// count the matches
var mismatch=0
// start loop through Logistics
for(var j = 0; j < data.length; j++){
// match Logistics: Event (Column A) and Trailer (Column B)
// match Main: Event (Column A) and Trailer (Column C)
// Compare data: if there is no match between 2 sheets, set "Trailer", "Position" and "Arrival date" to empty:
var logEventTrailer = data[j][0]+data[j][1];
var mainEventTrailer = dataMain[i][0]+dataMain[i][2];
//Logger.log("DEBUG: i:"+i+", Main:"+mainEventTrailer+", j:"+j+" Log:"+logEventTrailer);
if (mainEventTrailer === logEventTrailer){
// match
// Logger.log("DEBUG: Match-"+"i:"+i+", Main:"+mainEventTrailer+", j:"+j+" Log:"+logEventTrailer);
// if this is a match, then break loop and goto to next i
break;
}
else{
// no match
mismatch = mismatch+1
//Logger.log("DEBUG: No match:"+match+"-i:"+i+", Main:"+mainEventTrailer+", j:"+j+" Log:"+logEventTrailer)
}
// how many mismatches
if (mismatch ==data.length){
// no match found
//Logger.log("DEBUG: no match found");
// update array values for this row
dataMain[i][2] = "";
dataMain[i][3] = "";
dataMain[i][4] = "";
}
}
}
// update the array values for Main
tabMain.setValues(dataMain);
}

Related

If column contains text add a checkbox in column apps script google sheets

I am copying data from a spreadsheet titled after the specific month and placing it in my main spreadsheet. I have successfully copied the data into range K80:K94 on my Daily Hub sheet.
In range K80:K94 I now want to add a checkbox in column M if there is a value in column K. For example if there is a value in K80 and K81 there would be a checkbox in M80 and M81. I feel like this should be fairly straightforward, however I have tried a few different options including using IsBlank() and nothing seems to be working.
function dailyhubhabits() {
var montha = new Array(12);
montha[0] = "JANUARY";
montha[1] = "FEBRUARY";
montha[2] = "MARCH";
montha[3] = "APRIL";
montha[4] = "MAY";
montha[5] = "JUNE";
montha[6] = "JULY";
montha[7] = "AUGUST";
montha[8] = "SEPTEMBER";
montha[9] = "OCTOBER";
montha[10] = "NOVEMBER";
montha[11] = "DECEMBER";
var dailyhabitshubmonth = new Date();
var getdhmonth = montha[dailyhabitshubmonth.getMonth()];
Logger.log(getdhmonth);
var mhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(getdhmonth);
var monthhabitsogdata = mhs.getRange("C56:E70");
var gethabits = monthhabitsogdata.getValues();
Logger.log(gethabits);
var dhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DAILY HUB");
var habitsarea = dhs.getRange("K80:K94");
monthhabitsogdata.copyTo(habitsarea);
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
}
You want to insert a checkbox on Column M when there is a value in the same row of column K.
There are two problems with this part of your script:
evaluating whether the cell has a value
defining the target range for the checkbox
Does the cell have a value?
length returns the number of records in an array, but it is not a good method for determining whether a cell contains a value. This is a popular topic; you might care to read Google Spreadheets Scripts: check if cell is empty for several methods.
a better approach is !== ""
Defining the target cell
dhs.getRange(i+1,14).insertCheckboxes(); - there are two problems here
Column M is 13
i starts at zero, so the first range value would be .getRange(1,14) = Cell N1.
so you need a variable that defines the startRow, such as:
var startRow = 80
REPLACE
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
WITH
var startRow = 80
var endRow = 94
var datavalues = dhs.getRange("K"+startRow+":K"+endRow).getValues()
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0] !=="") {
dhs.getRange(i+startRow,13).insertCheckboxes()
}
}
SUGGESTION
In my understanding, here's your goal:
Check values in K80:K94
Insert a checkbox on a row in M that is adjacent to a row that isn't empty in the K80:K94 range.
Perhaps you could try this sample script to replace your current line on the section in inserting the check-boxes:
/** SUGGESTION
* 1. Iterate through the values in range K80:K94 & identify which aren't empty.
* 2. Get each non-empty values' row numbers.
* 3. To reduce runtime execution in the loop, if there are consecutive non-empty values, set them as a range (e.g. M80:M81). Otherwise a single value will be set as a single range (e.g. M83);
* 4. Iterate through these ranges & insert the checkboxes.
*/
var range = SpreadsheetApp.getActive().getRange('K80:K94');
var temp_values = range.getValues().map((x, i) => x != '' ? [x, (range.getLastRow() - (range.getNumRows() - i) + 1)].flat() : '*');
var ranges = temp_values.join().split('*').map(y => (y.replace(/[a-zA-Z,]+/g, '-')).split('-').filter(x => x != ''));
ranges.map(z => [...new Set([z[0], z[z.length - 1]])]).forEach(
row => row.length > 1 ? SpreadsheetApp.getActive().getRange(`M${row[0]}:M${row[1]}`).insertCheckboxes() :
SpreadsheetApp.getActive().getRange(`M${row[0]}`).insertCheckboxes()
);
/** End */
This sample script runs faster vs your current implementation as it shortens the data to be processed in the loop
Demo
Sample sheet
After running the script

Moving partial data from one row to another row in same sheet by inserting new row just below

Basically what I am looking for is if I have data in column 9 of each row, I want to split that row into two rows. I do not want to scroll through to see the entire data. If column 9 has data, then cut the data from column 9 to the last column and insert a row below to paste that cut data. I have written a piece of code, but it isn't working.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var rangeData = ss.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var data = ss.getRange(2, 1, lastRow, lastColumn).getValues();
var getRow = rangeData.getRow()
for (var i = 0; i <= lastRow; i++) {
var row = data[i];
Logger.log('data row ' + row);
var rangeToMove = ss.getRange(/*startRow*/ i, /*startColumn*/ 9, /*numRows*/ 1, /*numColumns*/ ss.getMaxColumns());
ss.insertRowAfter(i);
rangeToMove.moveTo(ss.getRange(i++,2,2,9));
}
}
Any help will be much appreciated.
I understand that you want to split the rows in nine columns chunks, writing the row surplus immediately under the original row. If my understanding is correct, the following code will work for your scenario. I saw in your code that you don't want to interact with the first row; I respected that decision in my code.
function so61670015() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var dataRange = sheet.getDataRange();
var lastColumn = dataRange.getLastColumn();
var lastRow = dataRange.getLastRow();
var rawData = sheet.getRange(2, 1, lastRow - 1, lastColumn).getValues();
var maxColumn = 9; // Column I
var data = [];
// Step I - Clean
for (var j = 0; j < rawData.length; j++) {
data.push(rawData[j].filter(value => value != ""));
}
// Step II - Wrap
for (var j = 0; j < data.length; j++) {
if (data[j].length > maxColumn) {
var surplus = data[j].slice(9);
data[j].splice(9);
data.splice(j + 1, 0, surplus);
}
}
// Step III - Fill
for (var j = 0; j < data.length; j++) {
if (data[j].length < maxColumn) {
var emptyColumns = maxColumn - data[j].length;
for (var s = 0; s < emptyColumns; s++) {
data[j].push("");
}
}
}
// Step IV - Overwrite
sheet.getRange(2, 1, lastRow, lastColumn).setValue("");
sheet.getRange(2, 1, data.length, 9).setValues(data);
}
I commented on the different sections of the code to make it clearer. In the initial step I declared all the variables that will be needed later. This part of the code uses the same Spreadsheet methods as your original code.
In the first step the code will clean all the void values of the data (void as in "", not null). This will help to differentiate empty cells from filled ones. I used the method .filter() to write in the array data only the non-void values.
Then the code will cut every row at the maximum column size (column I in your question) and write the row surplus in a new row below. The code performs that action with the methods .slice() and .splice() (to read and modify the array respectively). Please, note how I use .splice() twice: first to modify the row by removing every cell over the column limit, and later to modify the data array to insert a row with the surplus cells. If the surplus is still longer than the maximum allowed, then it will repeat the operation until all rows adhere to the column limit.
After those procedures, the data need to be filled again with void values (as in "") in those rows that ended up being smaller than the column limit. This action will be required to write the data later, because the method .setValues() requires the array of values being of equal size of the selected range. To accomplish that I used the .push() method to insert void values in every row until the column limit is reached.
Finally the data is ready to be written. In a two steps process, the code will first clear the original sheet and write the final data immediately after. This marks the end of the operation.
To illustrate the usage of this code, if we use the following sample data:
We will end up with this sheet:
Please, ask me any question if you need further help or clarifications.

Looping through rows, get values, and add to total on another sheet

So I'm stumped on this in Google Sheets.
Sheet 'Price Calculator' Qty has a of items bought and sold in Column A, separated into 2 named ranges TRADE_QTY and BUY_QTY.
An identical List appears in sheet 'Master Tally', with qtys from previous trades, also in column A.
Have been flipping through multiple windows of examples of code and none seem to be able to provide anything that works.
function TEST() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
//Gets number of rows of each range
var Rows1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getNumRows()
var Rows2 = ss.getRange('\'PRICE CALCULATOR\'!BUY_QTY').getNumRows()
//Gets Starting rows of each range
var Row1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getRow()
var Row2 = ss.getRange('\'PRICE CALCULATOR\'!BUY_QTY').getRow()
for (r=Row1; r<Rows1; r++) {
ss.setActiveSheet(ss.getSheetByName('PRICE CALCULATOR'), true);
var ADD = ss.getRange(r,1).getValue()
if (cell.isBlank()) {
next r
}
else {
ss.setActiveSheet(ss.getSheetByName('Master Tally'), true);
var EXIST = ss.getRange(r,1).getValue()
var TOT = ADD+EXIST
ss.getRange(r,1).setValue(TOT)
}
}
}
Basically i'm try to develop a macro/script that adds the new trade qtys in sheet 'Price Calculator' to the existing qtys in 'Master Tally'
I"m stumped as it keeps throwing me 'Cannot find method getRange(number,number)' and now i'm out of my depth!
Link to the document;
https://docs.google.com/spreadsheets/d/1gIjCqv5KT41wYuJS1Hs1X8yPPUTPY_kGoTuilzxLkSo/edit?usp=sharing
This code suffers from a basic flaw: confusion between Row and Column numbers of an array (which start at 0-zero) with those derived from script commands such as getValue (which start at 1-one).
For example:
for (r=Row1; r<Rows1; r++) {
In this case, the value of Row1 was determined by getRow, so it returns the actual row number. But the loop values will generate the row and column number for an array starting at zero; so this line should read for (r=0; r<Rows1; r++) {
var EXIST = ss.getRange(r,1).getValue()
The purpose of this line is return the "existing qtys in 'Master Tally'", and the range will look in Column A for the value. However the values are actually in column B. So this line will never return an accurate value for "existing qtys".
There are some other things to note:
The existing code makes two getValue calls in every loop; these are time-expensive. The answer improves performance by getting the respective range values just once before the loop.
The update of the quantity sold (setValue(TOT)) is inside the loop. Again this is a time-expensive command. The answer updates the array values within the loop, and then updates the sheet once-only after the loop.
BUY Qty values are irrelevant
function so56017521() {
var ss = SpreadsheetApp.getActive();
//Gets number of rows of each range
var Rows1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getNumRows()
//Logger.log("DEBUG: Number of Rows: Trade Qty="+Rows1);
//Gets Starting rows of each range
var Row1 = ss.getRange('\'PRICE CALCULATOR\'!TRADE_QTY').getRow()
//Logger.log("DEBUG: Start Row: Trade Qty="+Row1);
// setup sheets
var calcsheet = "PRICE CALCULATOR";
var mastersheet = "Master Tally";
var calc = ss.getSheetByName(calcsheet);
var master = ss.getSheetByName(mastersheet);
var masterrows = master.getLastRow();
//Logger.log("DEBUG: Master Last Row = "+masterrows);
// get data for each sheet
var calcrange = calc.getRange(Row1, 1, Rows1);
var calcdata = calcrange.getValues();
var masterrange = master.getRange(3, 2, masterrows - 2);
var masterdata = masterrange.getValues();
//Logger.log("DEBUG: Calc data range = "+calcrange.getA1Notation()+", Master Data Range"+masterrange.getA1Notation());
for (r = 0; r < Rows1; r++) {
Logger.log("r=" + r);
var ADD = calcdata[r][0]; //Trade qty
//Logger.log("DEBUG: r="+r+", ADD value = "+ADD+", ADD.length = "+ADD.toString().length);
if (ADD.toString().length != 0) { // if Trade qty has value
// keep going
//Logger.log("DEBUG: keep going");
var EXIST = masterdata[r][0]; // existing quantity qty sold
Logger.log("DEBUG: r=" + r + ", EXIST = " + EXIST);
var TOT = ADD + EXIST; // sum of trade-in qty plus existing qty
Logger.log("DEBUG: ADD+EXIST = " + TOT);
// update masterdata array
masterdata[r][0] = TOT;
} else {
// nothing to see here
//Logger.log("DEBUG: next r please");
}
}
//update the spreadsheet with the adjusted array values
masterrange.setValues(masterdata);
}

Include adjacent cells when copying matched values to other sheet

I have a script that compares values in cells in two different sheets on a google spreadsheet. It can successfully match find the matches and copy them to a 3rd sheet. However, I want to expand the selection to the column left of the matches and copy over both the matches and adjacent cells.
For clarification: Spreadsheet 1 has emails and majors, spreadsheet 2 has majors under what college they belong to. I am matching the majors so I can get the emails of only the people in a specific college. Right now all it copies are the cells with the major, but I would like it to copy the cells with the email as well (which are one column to the left).
function match_CoE() {
//Set variables for active spreadsheet, input data, comparitive data, and finalized match input.
var sh = SpreadsheetApp.getActive();
var input = sh.getSheetByName('AOP 2018-2019').getRange("C1:C").getValues();
var values = sh.getSheetByName('Major-College-Conversion').getRange("A72:A75").getValues();
var output = sh.getSheetByName('College of Education Contacts');
var match = [];
//Compare Input to Pre-determined values for matches
for (i in input){
var setInput = input[i][0];
var exists = false;
for (j in values){
var setValues = values[j][0];
if (setValues == setInput){
exists = true;
break;
}
} // end for j
if (exists == true){
match.push([setInput])
}
}//end for i
//Copy matching values to new sheet.
output.getRange(1, 1, match.length, 1).setValues(match);
}
I expanded my search range to include the email list and shifted the set data I was matching against over a row. Then changed the setInput variable in the loop to +1 narrowing the search to just the majors and adding a second variable (setInputFina) to return just the emails I was able to generate the list!
function match_CoE() {
//Set variables for active spreadsheet, input data, comparitive data, and finalized match input.
var sh = SpreadsheetApp.getActive();
var input = sh.getSheetByName('AOP 2018-2019').getRange("B1:C").getValues();
var values = sh.getSheetByName('Major-College-Conversion').getRange("A72:B75").getValues();
var output = sh.getSheetByName('College of Education Contacts');
var match = [];
//Compare Input to Pre-determined values for matches
for (i in input){
var setInput = input[i][+1];
var setInputFinal = input[i][0];
var exists = false;
for (j in values){
var setValues = values[j][+1];
if (setValues == setInput){
exists = true;
break;
}
} // end for j
if (exists == true){
match.push([setInputFinal])
}
}//end for i
//Copy matching values to new sheet.
output.getRange(1, 1, match.length, 1).setValues(match);
}
function copySet_CoE() {
}

Automatically move data from one sheet to another in google docs

i have a spreadsheet that i keep track of tasks i need to do, once complete i enter a date in the last column. What i want is for that completed task to be moved to sheet 2.
At present i have sheet 1 named SUD_schedule and i want the completed row of data to be moved to sheet 2 named SUD_archive. I've looked through the forum posts already and i've tried a variation of scripts but so far no luck. The closest i have come is this script:
function onEdit() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();//Original sheet
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];//target sheet
// to act on only one sheet, check the sheet name here:
//If it si not first sheet, it will do nothing
if (sheet1.getSheetName() != "SUD_schedule") {
return;
}
//Get Row and column index of active cell.
var rowIndex = sheet1.getActiveRange().getRowIndex();
var colIndex = sheet1.getActiveRange().getColumnIndex();
//If the selected column is 10th and it is not a header row
if (colIndex == 16 && rowIndex > 1) {
//Get the data from the current row
var data = sheet1.getRange(rowIndex,1,1,9).getValues();
var lastRow2;
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
//Copy the data to the lastRow+1th row in target sheet
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
}
}
Column P (16) is the task complete date, row 1 is frozen and contains column headers.
Can anybody help show where i'm going wrong.
Kind regards
Den
Your code is not generic and you are more complicating your objective. Below will work out your need.
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('SUD_schedule');
var sheet2 = ss.getSheetByName('SUD_archive');
var dateColumn = "16";
var array = []
var range = sheet1.getRange(1, 1, sheet1.getLastRow(), dateColumn);
for (var i = 2; i <= sheet1.getLastRow(); i++) //i iterates from 2 as you say R1 is header
{
if(isValidDate(range.getCell(i, dateColumn).getValue()) == true) //checking if any values on column16 is valid date
{
data = sheet1.getRange(i, 1, 1, dateColumn).getValues(); //Getting the range values of particular row where C16 is date
for (var j = 0; j < dateColumn; j++) //Adding the row in array
{
array.push(data[0][j]);
}
}
if(array.length > 0)
{
sheet2.appendRow(array); //Appending the row in sheet2
array = [];
sheet1.deleteRow(i); //deleting the row in sheet as you said you want to move, if you copy remove this and next line
i=i-1; //managing i value after deleting a row.
}
}
}
//Below function return true if the given String is date, else false
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
I am not sure that the syntax you have as used below is entirely correct.
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
What I know will work for certain is if you omit the variable lastRow2 all together and use this instead.
sheet2.getRange(getLastRow+1,1,1,data[0].length).setValues(data);
To complement Joachin's answer, here is how you can adapt that code if you don't have the date in the last row. In the below shown part of the code replace Lastcolumnumber with your last column.
//Getting the range values of particular row where C16 is date
data = sheet1.getRange(i, 1, 1, LASTCOLUMNNUMBER).getValues();
//Adding the row in array
for (var j = 0; j < LASTCOLUMNNUMBER; j++)