In my google sheet file, sheet2 is being used as a blacklist wherein colA = a name, colB = date they were blacklisted, colC = date they will be removed from the blacklist (3 months after value in colB). Sheet1 records the responses of a signup form.
I would like a script that will automatically remove the names after their colC date. Perhaps a script that can delete an entire row if two cells match. An example would be if B2 and C2 are equal then delete row 2. This would have to apply to every row.
Any help would be greatly appreciated. Thank you very much.
It wasn't immediately clear which sheet you wanted to remove the name from. My answer assumes that you are removing the name from the blacklist sheet, sheet2.
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var sheet2 = ss.getSheetByName ("sheet2");
var values = sheet2.getDataRange ().getValues ();
var day = 24*3600*1000;
var today = parseInt ((new Date ().setHours (0,0,0,0))/day);
var ssdate; // Date to check, spreadsheet date
// Start at the bottom and move up the file, since all rows will shit up when deleted
for (var i = values.length; i >= 0; i--)
{
try
{
ssdate = values[i][2].getTime () / day; // 2 is for the C column (0 is for the A column)
}
catch (e) {ssdate = null;}
// Testing that the date is today or later to remove from file
if (ssdate && Math.floor (ssdate) >= today)
{
sheet2.deleteRow (i + 1); // Removes name from blacklist.
// i + 1, because it is the row number, which starts at 1
// where arrays start index at 0.
}
}
Related
I am really stuck to convert this idea to a script or a formula.
The task is sequential A then B and then C. I have this google sheet that has a column with only checkboxes. I want to click the checkbox when a payment is done. And then the sequence of things need to take place.
Pay count has to be increased by 1.
Renewal date has to be reset to the new date.
The Checkbox should go dimmed(unclickable) after the previous operations are done and remain there until there are only about 20 days left when it should become active(clickable) again.
Now there are formulae in some of the cells:
A. Pay Count column (contains how many times a payment is done) has this formula:
=IF(ISBLANK(PAIDON),,IF(RENEWON="",1,ROUNDUP(DAYS(RENEWON,PAIDON)/period)))
B. RENEWON column (calculates the next renewal date) has this formula:
=IF(ISBLANK(PAIDON),,IF(OR(SUBSCRIPTION="LifeTime",SUBSCRIPTION="OneTime"),,DATE(YEAR(L2),MONTH(L2)+IFS(SUBSCRIPTION="Yearly",12,SUBSCRIPTION="2Yearly",24,SUBSCRIPTION="3Yearly",36,SUBSCRIPTION="4Yearly",48,SUBSCRIPTION="5Yearly",60, SUBSCRIPTION="Monthly",1),DAY(L2)-1)))
You can understand that I am kind of a newbie here. So please do ask me for any information I missed here.
Need suggestions of how to convert the whole idea to a script or formula.
Any idea/guidance is helpful to me.
UPDATE: Additional info: My ranges are given in here for further help:
Also thanks for the right formatting! I definitely need lessons on them
you can get checkboxes column values first using :
// This function gets the full column Range like doing 'A1:A9999' in excel
// #param {String} column The column name to get ('A', 'G', etc)
// #param {Number} startIndex The row number to start from (1, 5, 15)
// #return {Range} The "Range" object containing the full column: https://developers.google.com/apps-script/class_range
function getFullColumn(column, startIndex){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
// sheet.setActiveSheet(sheet.getSheetByName('sheet_name')); //use this if you have different sheets
sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
return sheet.getRange(column+startIndex+':'+column+lastRow);
}
//make a call to the function to get all cells' values.
var checkboxesValues = getFullColumn('O', 1).getValues();
Now you have all the values, create a simple array and make for-loop on checkboxesValues length , where you check inside it if the value of the checkbox is TRUE or FALSE (checked - unchecked), and add it's index tp the newly created array.
example
var arr =[]
for (var i = 0; i < checkboxesValues.length; i++) {
if(checkboxesValues[i][0] === true){
arr.push(i +1); //adding row index (+1 because index start from zero in loop)
}
After that, you can make a new loop on the new array "arr" where it contains only true checkboxes values, and change values as you want.
So you go like:
for (var i = 0; i < arr.length; i++) {
//Step A (Pay count)
var payCount = SpreadsheetApp.getActiveSheet().getRange('N'+arr[i]).getValue()
payCount ++
SpreadsheetApp.getActiveSheet().getRange('N'+arr[i]).setValue(payCount);
//step B should go here
//Finally Step C
SpreadsheetApp.getActiveSheet().getRange('O'+arr[i]).setValue(false); // this will uncheck it
}
I think checkboxes can't be dimmed or disabled, they are only allowed to be checked or unchecked.
Step B, I don't really get it.
I hope this helps you by any means, and that I understood your question correctly.
Issue:
Every time a checkbox is checked, you want to do the following:
Update the Pay Count (+1).
Update the Date Paid with current date.
Update Renewal Due on date based on the currently existing formula.
If the difference between the current date and the renewal date is more than 20 days, disable the corresponding checkbox.
Also, you want to re-enable the checkbox when the renewable date is less than 20 days from now.
Solution:
There is no option for disabling checkboxes, but you just can remove them with removeCheckboxes().
In order to track when a checkbox is checked, I'd suggest you to use an onEdit trigger. This should (1) check if a checkbox was checked and, if that's the case (2) update the dates and (3) remove the corresponding checkbox if there're more than 20 days remaining. Check the code sample below for an example of how this could be done.
In order to enable the checkboxes again when the renewal date approaches (or to insert them again, which you can do with insertCheckboxes()), I'd suggest you to create a time-driven trigger which will periodically check the dates, and create the corresponding checkboxes.
I think, in this case, checking this once a day could be an appropriate periodicity. So you could use everyDays(n). This trigger can either be installed manually, or programmatically via executing the createDailyTrigger function below. Once the trigger is installed, the function enableCheckboxes (check code sample below) would run daily and check if the renewable date is less than 20 days from now (and insert the checkbox if that's the case).
Code sample:
function onEdit(e) {
const range = e.range;
const column = range.getColumn();
const row = range.getRow();
const value = e.value;
if (column === 15 && row > 1 && value == "TRUE") {
const sheet = e.source.getActiveSheet();
const countCell = sheet.getRange(row, 14);
countCell.setValue(countCell.getValue() + 1);
const now = new Date();
sheet.getRange(row, 12).setValue(now);
SpreadsheetApp.flush(); // Force update: pay date and renewable date
const renewalDate = sheet.getRange(row, 13).getValue();
// Remove checkbox if renewal date is more than 20 days from now
if (!isPaymentTime(renewalDate)) {
sheet.getRange(row, 15).removeCheckboxes();
}
}
}
function isPaymentTime(date) { // Check if renewal date is less than 20 days from now
const twentyDays = 1000 * 3600 * 24 * 20; // 20 days in milliseconds
const now = new Date();
if (date instanceof Date) return date.getTime() - now.getTime() < twentyDays;
else return false;
}
function enableCheckboxes() {
const sheet = SpreadsheetApp.getActive().getSheetByName("PODexpenses");
const firstRow = 2;
const renewalColumn = 13;
const numRows = sheet.getLastRow() - firstRow + 1;
const renewalDates = sheet.getRange(firstRow, renewalColumn, numRows).getValues().flat();
renewalDates.forEach((renewalDate, i) => {
if (isPaymentTime(renewalDate)) { // Check if less than 20 days
sheet.getRange(i + firstRow, 15).insertCheckboxes(); // Insert checkbox
}
})
}
function createDailyTrigger() {
ScriptApp.newTrigger("enableCheckboxes")
.timeBased()
.everyDays(1)
.create();
}
Note:
I saw there is another onEdit function in your current script. Please integrate this on the same function. There can only be one onEdit.
I'm trying to do a few things with a piece of script but cannot get it all down.
In row 2 of my sheet I have a series of consecutive dates based on the first of each month (British notation) e.g 01/08/2016, 01/09/2016, 01/10/2016. I then have a formula in rows 14 and 15 which I would like to be fixed (copy / paste value) when today's date matches that in row 2.
I feel that I need to run a few things -
Schedule a script to run once per day to check if any value in row 2 is equal to today's date. If true then...
Copy / paste values of the numbers in rows 14 and 15 and the column where the date matches.
Maybe an index/match is needed to verify part 1 but I'm really in the dark on how to do it.
Thanks for any assistance.
I think that I've now managed to crack it. In row 1 I have put in a helper formula which checks the date in row 2 against today's date and puts "TRUEMONTH" in the cell if it matches. My clunky code is then as follows (it still needs some tidying up for the sheet refs etc) -
function ColCheck() {
var name = "TRUEMONTH";
var name1 = "PASTE_ME_AS_VALUE";
var range = SpreadsheetApp.getActiveSheet().getDataRange()
var values = range.getValues();
var range1 = SpreadsheetApp.getActiveSheet().getDataRange()
var values1 = range1.getValues();
for (var row in values) {
for (var col in values[row]) {
if (values[row][col] == name) {
for (var row1 in values1) {
for (var col1 in values1[row1]) {
if (values1[row1][col1] == name1) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var row = parseInt(row1) + 1;
var col = parseInt(col) + 1;
var source = ss.getActiveSheet().getRange([row],[col]);
source.copyTo(ss.getActiveSheet().getRange([row],[col]), {contentsOnly: true});
}
}
}
}
}
}
}
Situation:
I have the following script, that run every 1 hours. This script insert "Update Age", I mean how many days past from DATE1 to DATE2.
Script 1: Script Link
Problem:
Column A: This column have an hyperlink. (Tickets# Numbers)
Column F & G: Date 1 and Date 2
Column H: The result of the Script 1
In the coulmn A, I have other script that insert Hyperlink... See Script Link 2.
This Script 2, run OnEdit but the Script 1 run every 1 hour.
When the script 1 run, the formula or Hyperlink is deleted only appear the data in cell.
Question:
Does exist any way that when the script 1 run, not delete the hyperlink or apply the hyperlink to all rows starting for the second 2 rows, the hyperlink.
SCRIPT FOR INSERT LINK:
function InsertLink(e)
{
var actSht = e.source.getActiveSheet();
if (actSht.getName() == ['ISP1']){
var activeCell = actSht.getActiveCell(); //Detec the ActiveCell
//var activeCell = event.range;
var activeCellValue = e.value;
var column = activeCell.getColumn();
var colNums = [1]; //Columns, whose edit is considered
if(colNums.indexOf(column) == -1) return; //If column other than considered then return
var row = activeCell.getRow();
if(row < 2) return; //If header row then return
var length = String(activeCellValue).length;
if (!e.value)
{
activeCell.setValue()
}
else if(length > 2)
{
activeCell.setValue('=HYPERLINK' + '("https://www.example.com/id='+activeCellValue+'";"'+activeCellValue+'")');
}
}
}
SCRIPT UPDATE:
// Create a timer trigger that will call "shellUpdateAge" every 30 minutes
// This function will run only for this particular sheets
function shellUpdateAge(){
var sheets = ['ISP1'];
for (var s in sheets){
toUpdateAge(sheets[s]);
}
}
function toUpdateAge(sheetName){
var ss = SpreadsheetApp.openById('1WmEwSLzqxOj7xkjokmor5B_HpMdabbEAGXiYeQwpIl8');
var sh = ss.getSheetByName(sheetName);
var data = sh.getRange(1,1,sh.getLastRow(),sh.getLastColumn()).getValues();
for(var n=0;n<data.length;++n){
if(typeof(data[n][6])=='object'){
data[n][7]=dayToToday(data[n][6])
}
}
sh.getRange(1,1,data.length,data[0].length).setValues(data)
}
function dayToToday(x){
var refcell = x;;// get value in column A to get the reference date
var refTime = new Date(refcell);
var ref = refTime.setHours(0,0,0,0)/(24*3600000);// set hours, minutes, seconds and milliseconds to 0 if necessary and get number of days
var today = new Date();
var TD = today.setHours(0,0,0,0)/(24*3600000);// set hours, minutes, seconds and milliseconds to 0 if necessary and get number of days
var day = parseInt(TD-ref);// get the difference in days (integer value )
return day ; // return result that will be in cell
}
DATA:
A2: =HYPERLINK("https://www.example.com/id=12345";"12345")
Note: The Hyperlink appear for every row in the column A when I ingress the Ticket ID.
F: 12/08/2014 18:08:00 (This Value is the DATA2)
G: 13/08/2014 18:08:00 (This Value is the DATE2)
H: The update age, insert the result from DATE2-DATE1. In this example the script will return 1 day.
Now the problem is when the script Update Age run, the script update the column G and delete the formula in column A.
I would just get and set the bare minimum, so you're not involving the HYPERLINK column:
function toUpdateAge(sheetName){
var ss = SpreadsheetApp.openById('1WmEwSLzqxOj7xkjokmor5B_HpMdabbEAGXiYeQwpIl8');
var sh = ss.getSheetByName(sheetName);
var range = sh.getRange(1,7,sh.getLastRow(),2);
var data = range.getValues();
for(var n=0;n<data.length;++n){
if(typeof(data[n][0])=='object'){
data[n][1]=dayToToday(data[n][0]);
}
}
range.setValues(data);
}
As an aside, you wouldn't consider a spreadsheet array formula for the date difference and HYPERLINK columns?
How to Insert Result of SUM from Two Date in the spreadsheet
Data:
H3: 27/11/2013 1:31:00
F3: 00:15
I3: Should be appear the result of SUM (H3+F3) using this formula =SUM(H3+F3). The Result is 27/11/2013 1:49:00 (24hs Formatting)
Action:
Should be executed only when some insert a value in the column F starting 3rd row.
Only should be executed for the row where was modify.
Should be insert the result in column I, the sum of H+F
Here I have the starting script for the 1 & 2.
function CreationDate(event){
//Script Sume Date
var actSht = event.source.getActiveSheet();
if (actSht.getName() == "sheet1"){
var activeCell = actSht.getActiveCell(); //Detec the ActiveCell
var column = activeCell.getColumn(); // Detect the Column of the ActiveCell
var colNums = [6]; //Coulmns, whose edit is considered
if(colNums.indexOf(column) == -1) return; //If column other than considered then return
var row = activeCell.getRow(); //Detect the ActiveRow
if(row < 3) return; //If header row then return
TEST:
I try to formatting this script Clic Here to sum the data and back the result in dd/mm/yyyy hh:mm:ss but I didn't have lucky.
Why is needed?:
Is very important have this formula run asap because I use is to scheduling a critical call to many ISP around the country.
I try to use =arrayformula(sum(h3+f3)) but didn't work. I needs a script because I add new rows all the time.
I will appreciate your help.
Best Regards,
The single-row version of Adam's formula, in row 3 for example, is:
=IF(ISNUMBER(H3)*ISNUMBER(F3);H3+F3;IFERROR(1/0))
Since you're worried that users may damage the formula, you can use an onEdit() trigger function to ensure the formula is updated in Column I anytime the data in Column F is edited.
// When a value is entered in column F, set I = H + F, for rows >= 3.
function onEdit(e) {
if (!e) { // This block is for testing in debugger; always uses row 3
e = {};
e.range = SpreadsheetApp.getActiveSheet().getRange('F3');
e.value = e.range.getValue();
}
var row = e.range.getRow();
var col = e.range.getColumn();
if (col == 6 && row >= 3) {
// Insert single-cell version of Adam's formula into I
e.range.getSheet().getRange(row,9)
.setFormula('=IF(ISNUMBER(H'+row+')*ISNUMBER(F'+row+');H'+row+'+F'+row+';IFERROR(1/0))');
}
}
An alternative way to insert the correct row number into the formula is to use Regular Expression replacement:
...
// Insert single-cell version of Adam's formula into I
var rowTag = new RegExp('%ROW%','g');
var formula = '=IF(ISNUMBER(H%ROW%)*ISNUMBER(F%ROW%);H%ROW%+F%ROW%;IFERROR(1/0))'
.replace(rowTag,row);
e.range.getSheet().getRange(row,9).setFormula(formula);
...
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++)