Working in Google Scripts, I'm trying to create a function that will look at the names of all tabs in a Google Sheet, and delete all sheets that meet these conditions: 1) tab name include a date (sheet names that do have a date are prefaced with some other text - the mm/dd/yyyy formatted date is in the string) and 2) the date in that sheet name is older than today's date minus 2 days).
There are two sheet names that include dates: "Leadership Review mm/dd/yyyy" and "Leadership Review w/notes mm/dd/yyyy". I have a script that auto-runs DAILY to create these sheets, so the goal is to automate a one-time clean-up for old sheets and set up a daily trigger to auto-run that function.
So far, I've created an array to capture the names of each sheet name ("tabNameArray") and have a regexp for use in matching for mm/dd/yyyy text that shows up in a sheet name.
My thought on how this would work - not sure how to accomplish 2 and 3:
create that array
parse the array - match each sheetname in the array against the regexp mm/dd/yyyy to identify sheetnames with a date.
A loop through that array... IF that sheetname has a date (create a new array or subarray with just those? doesn't seem necessary, but a thought), AND that date is > 2 days from today(), delete those sheets.
function deleteOldReportSheets() {
var sheetNameArray = new Array();
var sheetWithDatesArray = new Array(); //not sure this is necessary
var dateRegex = new RegExp("[0-3]?[0-9]\/[0-3]?[0-9]\/(?:[0-9]{2})?[0-9]{2}"); //for mm/dd/yyyy match, tested successfully on regex101.com
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var i=0 ; i<sheets.length ; i++) sheetNameArray.push( [ sheets[i].getName() ] ) // populates the sheetNameArray with names of each sheet
(do this: for sheets that have a date that is older than 2 days from today, delete...)
I'd appreciate support to code this - even better if there's a more efficient way than what I started thinking through.
Thanks for your assistance.
Description
The following example will compare the named sheets containing the date to today and indicate which sheets should be deleted. I leave the deletion operation to the OP.
Script
function test() {
try {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheets = spread.getSheets();
let dateRegex = new RegExp("[0-3]?[0-9]\/[0-3]?[0-9]\/(?:[0-9]{2})?[0-9]{2}"); //for mm/dd/yyyy match, tested successfully on regex101.com
let today = new Date();
console.log("today = "+Utilities.formatDate(today,"GMT","MM/dd/yyyy"));
today = new Date(today.getFullYear(),today.getMonth(),today.getDate()-2);
for( let i=0; i<sheets.length; i++ ) {
let sheet = sheets[i];
let name = sheet.getName();
console.log("Sheet name = "+name);
let match = name.match(dateRegex);
if( match ) {
match = new Date(match[0]);
if( match.valueOf() <= today.valueOf() ) {
console.log("delete");
}
else {
console.log("keep");
}
}
}
}
catch(err) {
console.log(err);
}
}
Console.log
11:45:16 AM Notice Execution started
11:45:17 AM Info today = 05/01/2022
11:45:17 AM Info Sheet name = Sheet1
11:45:17 AM Info Sheet name = Sheet 04/29/2022
11:45:17 AM Info delete
11:45:17 AM Info Sheet name = Sheet 04/30/2022
11:45:17 AM Info keep
11:45:17 AM Info Sheet name = Sheet 05/01/2022
11:45:17 AM Info keep
11:45:16 AM Notice Execution completed
Reference
Date()
String.match()
I think it can be something like this:
function myFunction() {
const reg = new RegExp(/\d{2}\/\d{2}\/\d{4}/);
const ss = SpreadsheetApp.getActiveSpreadsheet();
const if_two_days_ago = date =>
new Date(date).valueOf() < new Date().valueOf() - 3600000 * 48;
ss.getSheets()
.filter(s => reg.test(s.getName()))
.filter(s => if_two_days_ago(s.getName().match(reg)))
.forEach(s => { console.log('bye-bye -->', s.getName()); ss.deleteSheet(s) });
}
Delete Sheets with dates older than two days
function delSheets() {
const ss = SpreadsheetApp.getActive();
const dt = new Date();
const dtv = new Date(dt.getFullYear(),dt.getMonth(),dt.getDate() - 2).valueOf();//date threshold value
ss.getSheets().forEach(sh => {
let m = sh.getName().match(/\d{1,2}\/\d{1,2}\/\d{4}/g);//includes a date
if(m && new Date(m[0]).valueOf() < dtv) {
ss.deleteSheet(sh);
}
});
}
Related
I ve been getting some issues trying to use IF statement using items on the sheets as answers, mostly i been trying either using as firt statement a string directly or using another cell of the sheets with the statement comparing to the one i ve been trying to detect with out any results. Mostly my objective would be to, with a daly trigger, use an IF statement to detect every day if the date of today is the same as a date marked on a cell, my only current solution is and only thing is to make it detect if a cell is empty or not, so i just been using google Formulas that do the detect of the date, and if True make it empty, and if not make something appear
Here are some of the tests I ve been trying
Test 1:
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetName");
else if (Sheet.getRange(1, 1).getValue() == "2/2/2020"){}
(cell A1=2/2/2020)
Test 2:
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetName");
else if (Sheet.getRange(1, 1).getValue() == Sheet.getRange(1, 2).getValue()){}
(cell A1=2/2/2020)
(cell B1=2/2/2020)
Here are two solutions depending on what you are trying to accomplish.
This first one gets the values, formats them to a date format, and then compares them
function myFunction() {
var tz = Session.getScriptTimeZone();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('SheetName');
var data = sheet.getDataRange().getValues();
var aVal = Utilities.formatDate(data[0][0],tz,'MM/dd/YYYY');
var bVal = Utilities.formatDate(data[0][1],tz,'MM/dd/YYYY');
Logger.log([aVal,bVal])
if(aVal == bVal) {
//do something
}
}
This second section gets the display values and compares them. The first solution compares the date values while this second solution compares the dates as a string. Good luck!
function myFunction1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('SheetName');
var data = sheet.getDataRange().getDisplayValues();
var aVal = data[0][0]
var bVal = data[0][1]
Logger.log([aVal,bVal])
if(aVal == bVal) {
//do something
}
}
I am using .indexOf() to parse an array in a separate sheet via url and find a string in a header to return a numerical value or -1. Issue is I need to find the string match that is non case sensitive. ie var targetEmail = targetHeader.indexOf("Email"); would need to match "email", "user email", User email", etc. Normally I would use a regex but can't find a way to do this in Apps Script. Please any help would be very much appreciated.
sample code below:
var userHeader = USER.getRange(1, 1, 1, USER.getLastColumn()).getValues()[0];
var targetHeader = importSheet.getRange(1, 1, 1, importSheet.getLastColumn()).getValues()[0];
var ui = SpreadsheetApp.getUi();
var targetEmail = targetHeader.indexOf("Email");
if(targetEmail == -1)
ui.alert("Can not find Email column. Please rename column in source sheet.");
Using Javascript RegExp, you can use the following
let targetEmail = targetHeader.findIndex(h => /email/gi.test(h));
if(targetEmail == -1)
ui.alert("Can not find Email column. Please rename column in source sheet.");
Note: findIndex, let and the function shorthand will only work if your script is using the new V8 runtime.
I personally prefer to name the column I want using Google Sheets named ranges, and then use getNamedRanges(), so I'll be able to find the column number by its range name, regardless of the column heading text. If columns are inserted or deleted, the named range still points to the column you're looking for.
You could solve for this by
(1) joining the array into a string
targetHeader.join()
(2) and then changing the string to lowercase
toLowerCase()
and then looking for "email" (lower case).
var targetHeader = importSheet.getRange(1, 1, 1, importSheet.getLastColumn()).getValues()[0];
targetHeader = targetHeader.join().toLowerCase();
var targetEmail = targetHeader.indexOf('email');
Edit:
To find the column number with the word 'email' in it:
var importSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet101');
var targetHeader = importSheet.getRange(1, 1, 1, importSheet.getLastColumn()).getValues()[0];
var flag = false;
for (var c = 0; c < targetHeader.length; c++) {
var cellValue = targetHeader[c].toLowerCase();
if (cellValue.indexOf('email') > -1) {
flag = true;
break;
}
}
if (!flag) {
// ui alert that email not found
} else {
var columnWithEmalInHeader = c + 1;
// column with value 'email' found. Column number is c+1
// first time match of 'email' will pop-up here. All subsequent amounts will be ignored.
}
I am having trouble executing the Spreadsheet script below.
I think there are two mistakes but I do not know how to fix.
Could anyone help it to fix it?
1:Wildcard
if(original_date=='....-..-..')
2:if synteax
if(original_date=='....-..-..')
{condition="matched"}
Detail
On the spreadsheet, there are two columns.
The first columns have dates in a format as YYYY-MM-DD such as 2020-04-21.
But sometimes, they have different formats such as 04/21/2020.
The second columns are empty.
Only when the first column cell has the "YYYY-MM-DD" format, I want to copy the cell into the second cell in the second column.
*They have 10 rows.
Here is the script.
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1')
for(let i=1; i<=10; i++)
{
original_date_range = sheet.getRange(i, 1);
original_date = original_date_range.getValue();
cleaned_date_range = sheet.getRange(i, 2);
var condition = "";
if(original_date=='....-..-..')
{condition="matched"}
switch(condition)
{
case "matched":
cleaned_date_range.setValue(original_date);
case "":
cleaned_date_range.setValue("");
break;
}
}
I found the solution by myself.
1.Wildcard
The date "2020-04-25" can be written as /\d{4}.\d{2}.\d{2}/
2.If syntax
(Wildcard).test(string to check) is going to give you true/false output
In summary,
if you want to execute different tasks through the verification of date format such as 2020-04-25,
var sheet1 = SpreadsheetApp.getActive().getSheetByName('Sheet1')
var limit = sheet1.getLastRow()
for(let i=4; i<=10; i++) //You can add more rows if you have
{
var orignal_date_cell = sheet1.getRange(i, 1);
var orignal_date_cell_value = orignal_date_cell.getValue();
var target_date_cell = sheet1.getRange(i,2);
if ((/\d{4}.\d{2}.\d{2}/).test(orignal_date_cell_value))
{
target_date_cell.setValue(orignal_date_cell_value);
}
}
I have a questionaire that fills a google spreadsheet.
When I open the spreadsheet I want all commas (,) to be replaced by dots (.).
The name of my spreadsheet is 'GF Answers' and the sheet that it contains is 'responses'.
Any directions/suggestions for appropriate code?
Thanks!
To take Kriggs answer one step further, and simply drop you off at the destination, something like the following should work (you may have to change the 'range' to conform to your spreadsheet if there are any sections you don't want to modify):
function onOpen(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('responses');
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var data = range.getValues();
for (var row=0; row<data.length; row++) {
for (var item=0; item<data[row].length; item++) {
data[row][item] = data[row][item].replace(/,/g, '.');
}
}
range.setValues(data);
}
I need to replicate the Google Analytics date picker (plus a few new options). Can anyone tell me how to highlight all the cells on a calendar between two dates. My basic JavaScript is OK but I think I'm getting a bit out of my depth.
I'm using JQuery 1.5.1 and JQuery UI 1.8.14.
In needed to replicate Google Analytics date picker as well. I know you were asking just about highlighting cells, but if someone else would prefer complete solution, you can see my answer from another question: jquery google analytics datepicker
Here's a solution using the built-in 'onSelect' event (jsFiddle):
$(document).ready(function() {
'use strict';
var range = {
'start': null,
'stop': null
};
$('#picker').datepicker({
'onSelect': function(dateText, inst) {
var d, ds, i, sel, $this = $(this);
if (range.start === null || range.stop === null) {
if (range.start === null) {
range.start = new Date(dateText);
} else {
range.stop = new Date(dateText);
}
}
if (range.start !== null && range.stop !== null) {
if ($this.find('td').hasClass('selected')) {
//clear selected range
$this.children().removeClass('selected');
range.start = new Date(dateText);
range.stop = null;
//call internal method '_updateDatepicker'.
inst.inline = true;
} else {
//prevent internal method '_updateDatepicker' from being called.
inst.inline = false;
if (range.start > range.stop) {
d = range.stop;
range.stop = range.start;
range.start = d;
}
sel = (range.start.toString() === range.stop.toString()) ? 0 : (new Date(range.stop - range.start)).getDate();
for (i = 0; i <= sel; i += 1) {
ds = (range.start.getMonth() + 1).toString() + '/' + (range.start.getDate() + i).toString() + '/' + (range.start.getFullYear()).toString();
d = new Date(ds);
$this.find('td a').filter(function(index) {
return $(this).text() === d.getDate().toString();
}).parents('td').addClass('selected');
}
}
}
}
});
});
I became desperate and came up with a solution on my own. It wasn't pretty but I'll detail it.
I was able to construct a div that had the text boxes, buttons and the datepicker that looked like the Google Analytics control but I couldn't make the datepicker work properly. Eventually, I came up with the idea of creating a toggle variable that kept track of which date you were selecting (start date or end date). Using that variable in a custom onSelect event handler worked well but I still couldn't figure out how to get the cells between dates to highlight.
It took a while, but I slowly came to the realization that I couldn't do it with the datepicker as it existed out of the box. Once I figured that out, I was able to come up with a solution.
My solution was to add a new event call afterSelect. This is code that would run after all the internal adjustments and formatting were complete. I then wrote a function that, given a cell in the datepicker calendar, would return the date that it represented. I identified the calendar date cells by using jQuery to find all the elements that had the "ui-state-default" class. Once I had the date function and a list of all the calendar cells, I just needed to iterate over all of them and, if the date was in the correct range, add a new class to the parent.
It was extremely tedious but I was able to make it work.