In the following example I use Apps Script to schedule Google calendar events based on spreadsheet input. This code works just fine however I need to tweak it to do a small manipulation on the source sheet as well.
You can see here that I filter the range to only include rows that have "Pending" value in column D (r[3]). However I need to include a line of code in the loop so that after the filtered rows are synced to my Google Calendar the same cell value in column D changes to "Scheduled" for the respective cell. I have tried following this solution but could not implement it since I am new to JS.
Google Apps Script - .setValue in cell based on for loop matching
function calendarSync() {
var ss = SpreadsheetApp.getActiveSheet();
var calendarId = "My Calendar ID";
var eventCal = CalendarApp.getCalendarById(calendarId);
var eventArray = ss.getRange('A2:I500').getValues().filter(r => r[3] == "Pending Schedule");
for (x=0; x<eventArray.length; x++) {
var event = eventArray[x];
var eventName = event[0];
var startTime = event[1];
var endTime = event[2];
var exisEvents = eventCal.getEvents(startTime, endTime, {search: eventName}) //prevents creating duplicate events;
if (exisEvents.length == 0) {
eventCal.createEvent(eventName, startTime, endTime);
}
}
}
One simple solution is to change a bit logic of your script. Instead of using filter use an if statement then overwrite the whole range.
function calendarSync() {
var ss = SpreadsheetApp.getActiveSheet();
var calendarId = "My Calendar ID";
var eventCal = CalendarApp.getCalendarById(calendarId);
var eventArray = ss.getRange('A2:I500').getValues();
for (x = 0; x < eventArray.length; x++) {
var event = eventArray[x];
var eventName = event[0];
var startTime = event[1];
var endTime = event[2];
var status = event[3]; // Used to in the following comparison expression instead of filter
if (status === "Pending Schedule") {
var exisEvents = eventCal.getEvents(startTime, endTime, {
search: eventName
}) //prevents creating duplicate events;
if (exisEvents.length == 0) {
eventCal.createEvent(eventName, startTime, endTime);
eventArray[x][3] = "Scheduled"; // Update the status
}
}
}
ss.getRange('A2:I500').setValues(eventArray); // Overwrite the source data with the modified array
}
P.S. If you are using the default runtime instead of var it's better to use const and let, specially when writing complex scripts.
Related
I am trying to select an array from google sheets to create google calendar events based on that. The code chunk below runs just fine and gets the job done. But I want to be able to only select the range that has value of "select" in their column D.
I know it is probably a very easy answer but I am new to JS.
function calendarSync() {
var spreadSheet = SpreadsheetApp.getActiveSheet;
var eventCal = CalendarApp.getCalendarById(calendarId);
// Below instead of selecting the entire range I only need the rows that have a value of "select" in their D cell.
var eventArray = spreadSheet.getRange("A1:D100").getValues();
for (x=0; x<eventMatrix.length; x++){
var calEvent = eventArray[x];
var eventName = calEvent[0]
var startTime = calEvent[1];
var endTime = calEvent[2];
eventCal.createEvent(eventName, startTime, endTime);
}
In your situation, how about the following modification?
From:
var spreadSheet = SpreadsheetApp.getActiveSheet;
var eventCal = CalendarApp.getCalendarById(calendarId);
// Below instead of selecting the entire range I only need the rows that have a value of "select" in their D cell.
var eventArray = spreadSheet.getRange("A1:D100").getValues();
To:
var spreadSheet = SpreadsheetApp.getActiveSheet(); // Please add ()
var eventCal = CalendarApp.getCalendarById(calendarId);
var eventArray = spreadSheet.getRange("A1:D100").getValues().filter(r => r[3] == "select");
By this modification, eventArray has the values that select is included in the column "D".
Reference:
filter()
column B gets data updated
column C onwards is used to store consolidated data
im trying to copy a range of data to right next column
and update column C with data from column B
function doUpBCL() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('BLC');
var data_new = ss.getRange('B1').getDisplayValue();
var data_old = ss.getRange('C1').getDisplayValue();
if( data_new !== data_old ) // check if data changed
{
var lr = ss.getLastRow();
var lc = ss.getLastColumn();
var data = ss.getRange(1,3,lr,lc - 3).getValues();
ss.getRange(1,4,lr,lc - 4).setValues(data);
var data_ = ss.getRange(lr,2,1,1).getValues();
ss.getRange(lr,3,1,1).setValues(data_);
}
};
macro (created by Google Sheets) as EXAMPLE to ilustrate what i want is (i need help with above function, not this)
function upblc_macro() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('BLC'), true);
spreadsheet.getRange('D:D').activate();
spreadsheet.getRange('C:Z').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
spreadsheet.getRange('C:C').activate();
spreadsheet.getRange('B:B').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
using last row cos im going to use similar funcions to other pages and im trying to funcion where i dont have to adjust everytime
simply add new column, move or append to last column wont work the way i need
help is much appreciated, thanks
with lots of tries and errors, with a few tweaks and minor changes, i managed to make it work as i wanted
function doUpBCL() {
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('BLC');
const data_new = ss.getRange('B1').getDisplayValue();
const data_old = ss.getRange('C1').getDisplayValue();
if( data_new !== data_old ) // check if data changed
{
var lr = ss.getLastRow();
var lc = ss.getLastColumn();
const data = ss.getRange(1,3,lr,lc-2).getValues();
ss.getRange(1,4,lr,lc-2).setValues(data);
const data_ = ss.getRange(1,2,lr,1).getValues();
ss.getRange(1,3,lr,1).setValues(data_);
}
else
{
}
};
not sure why of some stuff i changed, some other stuff i learned, but still learning
I have a google sheets spreadsheet. Row 2 contains dates e.g. 25/08/2020, 26/08/2020 going across many columns. Is there a script I can run to make it jump to the cell containing the current date when the document is first opened?
I know there is OnOpen() method which you define and it runs on opening the document, however, it is getting the code that actually works that's proving difficult.
Note: I have looked at Google spreadsheet / docs , jump to current date cell on Open but the solutions don't work (I assume due to me having my dates all in one row).
I don't know javascript really well, I understand a little of the basics. Any help would be much appreciated.
Thanks
The code you found at Google spreadsheet / docs , jump to current date cell on Open does not work for you as it only checks the first column.
I modified this code a little to search for dates on a row. Change rowWithDates variable as needed.
function onOpen() { // runs automatically
var rowWithDates = 2; // change as needed
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var range = sh.getDataRange()
var data = range.getValues();
var today = new Date().setHours(0,0,0,0);
var diffref = today;
var diff;
var idx;
for(var n=0;n<range.getWidth();n++){
var date = new Date(data[rowWithDates-1][n]).setHours(0,0,0,0);
diff=today-date;
if(diff==0){break}
Logger.log("diffref = "+diffref+" today-date = diff = "+diff);
if(diff < diffref && diff > 0){idx=n ; diffref=diff}
}
if(n==data.length){n=idx}
n++;
sh.getRange(rowWithDates, n).activate();
}
You can use the code that was provided in the answer you cited in your question, you just need to change a couple of things:
Make it look in a row, rather than a column (note that the data array is changing the second array dimension, rather than the first); and
Make it look in a specific row, rather than just hardcoded to the first (you could just, instead of 0, have the array use a variable "row"; instead, I just had the code pull the data for only the row with dates - this is faster for very large spreadsheets).
function onOpen() {
var row = 8; //set this to be the row with the dates
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var data = sh.getDataRange();
var datesrow = sh.getRange(row,data.getColumn(),row,data.getWidth()).getValues();
var today = new Date().setHours(0,0,0,0);
for(var n=0;n<datesrow[0].length;n++){
var date = new Date(datesrow[0][n]).setHours(0,0,0,0);
if(date==today){break};
}
console.log(n);
n++;
sh.getRange(row,n).activate();
}
Solution:
While the other solutions might work for now, when working with Dates is recommended to consider display values instead. It is also highly recommended to get rid of old for loops and var declarations.
This will be a more futureproof solution:
function onOpen() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh =ss.getActiveSheet();
const today = new Date();
const today_year= today.getFullYear();
const today_month = addZero(today.getMonth()+1);
const today_day = addZero(today.getDate());
const today_date = today_day.toString() + "/" + today_month.toString() + "/" + today_year.toString();
function addZero(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
const dates = sh.getRange(2,1,1,sh.getLastColumn()).getDisplayValues().flat(1);
dates.forEach((d,index)=>{
if (d===today_date){
sh.getRange(2,index+1).activate();}});
}
References:
getDisplayValues()
I'm trying to check whether there are duplicate phone numbers in a column and to then return the first id number associated with the phone match in google apps script.
I created an if statement, according to every reference I've checked I've coded it right. The script runs but nothing populates??
I defined the ranges for the count that has the phone number and id number associated with it. I ensured its in the correct array my using map and used a basic if else statement.
Everything seems to run but the statement doesn't throw out the id numbers. In fact nothing happens.
Please help...
function myFunction() {
var sss = SpreadsheetApp.openById('spreadsheetid'); // sss = source spreadsheet
var ss = sss.getSheetByName('spreadsheetname'); // ss = source sheet
var lr = ss.getLastRow();
for (var i=3;i<lr;i++) {
var phone = ss.getRange(i,4,lr).getValues();
var id = ss.getRange(i,1,lr).getValues();
var phonearray = phone.map(function(r){return r[0]});
if(phonearray == phone[i]){
//emailarray.indexOf(email[i]) &&
ss.getRange(i,30,lr).setValue(id[i]);
} else
ss.getRange(i,30,lr).setValue("");
}
}
There are some things you need to consider
A valuerange is a 2-dimensional array, thus to access the value of an individual cell you need to define phone[i][0] instead of phone[i]
The method getRange requires the syntax getRange(startRow, startColumn, numRows) rather than getRange(startRow, startColumn, lastRow). Keep this in mind when you retrieve your range of interest.
Searching for duplicates will require in your case two (nested) for loops.
Please find below a sample of how you can modify your code to achieve the desired functionality:
function myFunction() {
var sss = SpreadsheetApp.openById('spreadsheetid'); // sss = source spreadsheet
var ss = sss.getSheetByName('spreadsheetname'); // ss = source sheet
var lr = ss.getLastRow();
var phones = ss.getRange(3,4,lr-3+1).getValues();
var ids = ss.getRange(3,1,lr-3+1).getValues();
var phonearray = phones.map(function(r){return r[0]});
for (var i=0;i<=(lr-3);i++) {
var val = phones[i][0];
var duplicates = [];
for(var j = 0; j < phonearray.length; j++){
if (phonearray[j] == val){
duplicates.push(j);
}
}
if(duplicates.length>1){
Logger.log("duplicate");
//emailarray.indexOf(email[i]) &&
ss.getRange((i+3),30).setValue(ids[i]);
} else{
Logger.log("no duplicate");
ss.getRange((i+3),30).setValue("");
}
}
}
I am using a Google Form to schedule appointments based on open events on my Google Calendar. I've sectioned off time slots on the calendar and if the title of the event is "Open", then the time slot can be used to create an event. I would next like to know if it's possible to override and delete the existing open event with the new appointment that is created.
Here is the code I am using to push events to my calendar as they are created in the form:
var calendarID = "";
var startDtID = 5;
var endDtID = 5; // Column containing date/time
var nameID = 2; // Column containing name
var emailID = 3; // Column containing email
var phoneID = 4; // Column containing phone
var formTimeStampID = 1; // column containing timestamp
function getLatestAndSubmitToCalendar() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var lr = rows.getLastRow();
var startDt = sheet.getRange(lr,startDtID,1,1).getValue();
var endDt = sheet.getRange(lr,endDtID,1,1).getValue(); // remove hour and minute for the start/end time
var desc = "Comments :"+sheet.getRange(lr,phoneID,1,1).getValue(); // set comments as description, add in timestamp and submission
var title = sheet.getRange(lr,nameID,1,1).getValue()+" - "+sheet.getRange(lr,emailID,1,1).getValue(); // create title using name and email
createEvent(calendarID,title,startDt,endDt,desc);
}; // run create event function
function createEvent(calendarId,title,startDt,endDt,desc) {
var cal = CalendarApp.getCalendarById(calendarID);
var start = new Date(startDt);
var end = new Date(endDt);
var event = cal.createEvent(title, start, end, {
description : desc
}); // set options for event
};
The CalendarEvent class has a method to delete events through deleteEvent(), but I am confused as to how I can determine the right event to be deleted. How can I match up the date and time of the current event with that of the one that already exists so I can override it?
You'll need to call calendar.getEvents() first, then loop through the event(s) you want to delete, and call .delete() on each of them. Here's the API link: https://developers.google.com/apps-script/reference/calendar/calendar#getEvents%28Date,Date,Object%29
Something like this:
var now = new Date();
var twoHoursFromNow = new Date(now.getTime() + (2 * 60 * 60 * 1000));
var events = CalendarApp.getDefaultCalendar().getEvents(now, twoHoursFromNow,
{search: 'meeting'});
for(int i = 0; i < events.length; i++){
if this is the right event (compare title, times, etc?){
events[i].deleteEvent();
}
}
In this case, you'll probably want to compare the descriptions. Also note that you can pass options to the getEvents call; one you can use to filter during the search is the search parameter like I did above. It filters the returned events on that text.
EDIT: Ideally, you'll want to use this: https://developers.google.com/apps-script/reference/calendar/calendar#getEventSeriesById%28String%29
That gets the event by the ID; I answered with the assumption you didn't have the ID stored anywhere though. If you do, all the better, and you'll be certain you're replacing the right event.
It works! Here is my completed code for adding an event and deleting one that occurs at the same time.
function createEvent(calendarId,title,startDt,endDt,desc) {
var cal = CalendarApp.getCalendarById(calendarID);
var start = new Date(startDt);
var end = new Date(endDt);
var event = cal.createEvent(title, start, end, {
description : desc
});
var events = cal.getEvents(start, end, {
search: 'open'
});
for (i in events){
events[i].deleteEvent();
}
};