I need LOTS of help on this one, but I think it's a procedure that would be helpful to other green-programers in the future.
I have a Google Form for people to fill that are new to our neighborhood. It, obviously, feeds a Google Docs spreadsheet. (Here's a link to a copy of the sheet that's just edited to black-out some of the personal data:
https://docs.google.com/spreadsheet/ccc?key=0AjsoIob8dJfodG9WN0ZmWUE1ek9rc3JrVFpDQ0J0OGc
)
One of the questions is which of our groups are you interested in joining. The Form asks the question, then allows the user to check off which of about 10 groups the new member is interested in joining. The data is then fed into the sheet listing all of the groups the new member wants to join in a single cell (that's column F).
The administrative task that I now need to tackle is going through all of the responses and pulling out the members who have said--for example--they're interested in joining the "Helping Hands" group, copying their e-mail address (which is in column X) and pasting it into the Google Groups sign-up sheet for that group.
This is understandable tedious. Especially because--again--we have about 15 of these Google Groups lists that need to be populated.
What would be great would be if I could write a little Google Apps Script that will go through the data in the sheet and give me a comma separated list of all the e-mail addresses that meet the correct criteria so that I could just copy-and-paste that into the Google Group sign up page.
In plain English, the regular expression should say this:
- IF column F contains Helping Hands
- AND IF column G does NOT contain Yes
- THEN add the contents of column X to an exported string, followed by a comma
- Repeat for every row in the sheet.
So here's the questions:
1) How do I do this?
2) How does this get triggered?
For the first step, I've already determined that the following regular expression will give me what I need:
/(^|, )Helping Hands($|, )/
I'm not sure how to augment that, though, to include the second required IF statement. I also don't know how to write the THEN statement. And--as I said above--I have NO idea how to trigger all of this.
Thanks in advance to anybody who can help! And the Newcomers to Sewickley also thank you! Please let me know if I can clear anything up.
Why don't you go further and set up a on form submit trigger that reads the groups and adds them automatically using the (yet experimental) Groups Services for Apps Script?
Such script is not very difficult to implement, maybe you should check the user guides and tutorials.
Looks like you are happy copy-pasting the list of email addresses if you get it in a comma separated format. So you can trigger it yourself when you need it.
You can use this code snippet
var options = ['Helping Hands','Book Club',...]; // Add all 10 options here
var GROUPS_COL = 5 ; //Column F
var COLG = 6;
var EMAIL_COL = 23 ; //Column X
var emailList = ['', '','',''...] ; // 10 blanks
var data = SpreadsheetApp.openById('ID_HERE').getSheetByName('SheetName').getvalues();
for (var i = 1; i < data.length ; i++){
for( var j = 0 ; j < options.length ; j++){
if (data[i][GROUPS_COL].indexOf(options[j]) != -1 &&
data[i][COLG] != 'Yes') {
// The entry in row i+1 has checked option[j]
if (emailList[j] != ''){
emailList[j] += ',' ; // Add your comma
}
emailList[j] += data[i][EMAIL_COL];
}
}
}
/* Now you have your comma separated list in emailList */
for ( var i = 0 ; i < 10; i ++){
Logger.log(option[i] + ':' + emailList[i])
}
Obviously, you have to tweak this code to suit your needs, but will give you a fair idea of how to proceed. The code has not been tested.
You have no idea how unbelievable stoked I am that this finally worked out. Thanks a million times over to #Sirik for sticking with it and writing the code and helping me debug it. It does EXACTLY what I want it to and it's just the coolest thing in the world to make it do EXACTLY what I wanted it to.
For the sake of posterity or anybody else who might want to be doing something similar to what I was doing, here's the exact code, all smoothed out, as implemented. You'll see that I added just a few extra bits at the end to e-mail the log to me and give me a dialogue box acknowledging that the script had run (I don't like that feeling of clicking the button and not SEEING something happen).
function Export_Groups_List() {
var options = ['1 & 2 Year Olds\' Playgroup','3 & 4 Year Olds\' Playgroup','Babies\' Playgroup','Book Club','Couples Night Out','Directory','Girls Night Out','Helping Hands','Membership','Newsletter','Serving Sewickley','Treasurer'];
var GROUPS_COL = 5; //This is column 5
var COLG = 6; //Column G
var EMAIL_COL = 23; //Column X
var emailList = ['', '', '', '', '', '', '', '', '', '', '', ''] //12 blanks
var data = SpreadsheetApp.openById('___').getSheetByName('Master').getDataRange().getValues();
for (var i = 1; i < data.length ; i++){
for( var j = 0 ; j < options.length ; j++){
if (data[i][GROUPS_COL].indexOf(options[j]) != -1 &&
data[i][COLG] != 'Yes') {
if (emailList[j] != ''){
emailList[j] += ', ' ;
}
emailList[j] += data[i][EMAIL_COL];
}
}
}
for ( var i = 0 ; i < 12; i ++){
Logger.log(options[i] + ': ' + emailList[i])
}
MailApp.sendEmail("matt#twodoebs.com", "Groups Export", Logger.getLog() + " \n\n" +
"At your request, you've been added to this Sewickley Newcomers & Neighbors Google Group." + "\n\n" +
"Using Google Groups allows us to always have up to date e-mail lists, make sure that people don't get bombarded with messages they don't want, and facilitate smooth communication between our members. If you have any questions about how to use the Google Groups service with your Newcomers & Neighbors membership, please send an e-mail to SewickleyNAN#gmail.com." + "\n\n" +
"Thank you and thanks for your participation!") ;
Browser.msgBox("OK. Check your e-mail for the export list. doebtown rocks the house!")
}
Evan Plaice provides a nice Google Apps Script that you can use in your own spreadsheet which essentially renders a menu that allows you to transpose a column using a "Text to Column" feature. https://webapps.stackexchange.com/questions/22799/text-to-columns-conversion-in-google-spreadsheets
Related
I'm working on a spreadsheet where data is added in real time by an API. The data from the API is from users who sign up for a newsletter consists of basic data. When data is send from de API, it is added as a new row in the spreadsheet.
Users also have the option to answer additional newsletter questions later, this will also cause the API to add a new row, with additional data that is placed in different columns, but also still show the existing data that was previously known.
To avoid clutter, I want to remove duplicates based on one column and keep the last entry in Google Sheets. Which results in removing the old basic data row and only keeping the row with additional data. To highlight that this is data that is "updated" by the user, I also highlight this row. The data used to mark submissions as duplicates will be based on a user's email address. Since this will remain the same in both cases. [I may have to be careful with uppercase and lowercase letters, where the script doesn't see two emails as duplicates, I don't have an answer for that yet]
Besides this I already have a script in place that adds current time and date to an added row and places it in the first colomn.
For the duplicate issue, I already found a simular question Remove duplicates based on one column and keep latest entry in google sheets and the solution from Tanaike was very helpfull. Overall this code works for me, but sometimes it seems that the script runs when it's not suppose to.
My current script looks like this:
function onChange(e){
if (e.changeType == 'INSERT_ROW'){
const sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sh.getRange(sh.getActiveRange().getRow(), 1).setValue(new Date());
}
}
function removeDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Inzendingen');
var dt = sh.getDataRange().getValues();
var uA = [];
for (var i = dt.length - 1; i >= 0; i--) {
if (uA.indexOf(dt[i][4]) == -1) {
uA.push(dt[i][4]);
} else {
sh.deleteRow(i + 1);
Utilities.sleep(500);
sh.getRange(sh.getLastRow(),1,1,sh.getLastColumn()).setBackground('lightblue');
}
}
}
I added Utilities.sleep(500); to prevent the scenario where the row that's being deleted was faster than the highlight. So to prevent having an empty highlighted row at the bottom underneath the latest entered row.
Both scripts are setup with the trigger: From Spreadsheet - On Change
If everything works as planned, it should work something like this (all fake data, no worries):
My problem is as follows:
Currently some new users that are being added by the API for the first time, are also being highlighted. I suspect this has something to do with the fact that the duplicate deletion also works when the value of the email colomn is empty. However, this is only an assumption given the limited knowledge I have of these matters.
Seen is this example:
Long story short
I would love for this script to work as I intend it to do, where it only removes duplicates based on a duplicate email adress in colomn E. It would be even better if the duplicate deletion script also ignores capitalization. And lastly that it also ignores blank entries in colomn E.
I tried to use the script Remove duplicates based on one column and keep latest entry in google sheets
And make some addition in this script. Stuff like:
function removeDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Inzendingen');
var dt = sh.getDataRange().getValues();
var uA = [];
for (var i = dt.length - 1; i >= 0; i--) {
if (uA.indexOf(dt[i][4]) == -1 && dt.length !=0 ) {
uA.push(dt[i][4]);
} else {
sh.deleteRow(i + 1);
sh.getRange(sh.getLastRow(),1,1,sh.getLastColumn()).setBackground('lightblue');
}
}
}
Where I thought adding && dt.length !=0 would signal to the "if" to only trigger when there's a duplicate and when the value/length is not 0.
If I understand you correctly, the only issue is around these new people with no emails being highlighted. I believe you are on the right track, but you have dt.length != 0, which is looking at the entire array. Instead, you want to just check for the email.
As such, you can use this:
dt[i][4].length != 0
or
dt[i][4] != ""
EDIT:
I believe this will give the results you want. Blank emails are ignored, and duplicate emails ignore case.
function removeDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Inzendingen');
var dtAll = sh.getDataRange().getValues();
dt = dtAll.map(function(f){return [f[4].toLowerCase()]});
var uA = [];
for (var i = dt.length - 1; i >= 0; i--) {
if (uA.indexOf(dt[i][0]) == -1) {
uA.push(dt[i][0]);
Logger.log(uA[i]);
} else if (dt[i][0] != ""){
sh.deleteRow(i + 1);
sh.getRange(sh.getLastRow(),1,1,sh.getLastColumn()).setBackground('lightblue');
}
}
}
I've got a Google Form where I ask several people to provide feedback for N people.
The questions in the Google Form will be the same for all feedback providers, but each feedback provider will be asked to provide feedback to different people.
I'd like to have multiple forms that I send out to feedback providers with the same questions but have the first drop-down populated with the list of their respective feedback recipients. Following that, I'd like the responses across all feedback providers to be collated in the same spreadsheet.
I tried the following code. I've built arrays of all feedback receivers for each feedback provider. I was hoping to use that array to populate the drop-down menu. It works, but through each iteration in the for loop, it overwrites the previous iteration. You can find the spreadsheet here.
var ssID = "179TI57n-yhSZ1TbBzw5IlHHVYIawyA4_g9uu_7sEIOk";
var formID = "1HvwvEaf3mCscVmYfrS4Zsn58pqznh8rvg4LCPLMBGMw";
function questionID(){
var arrQuestions = FormApp.openById(formID).getItems();
for (let i = 0; i < arrQuestions.length; i++) {
var questionID = arrQuestions[i].getId().toString();
Logger.log('entry.' + questionID + '&');
}
}
function providerList() {
var ssProviderList = SpreadsheetApp.openById(ssID).getSheetByName("Map Feedback Providers");
var arrProviderList = ssProviderList.getRange("A2:H").getValues();
// loop through each row in spreadsheet
for (let i = 0; i < arrProviderList.length; i++){
// create a blank array for each feedback provider to fill with the Avengers they need to provide feedback for
let arrAvengers = [];
// loop through each row starting at column C
for (let j = 2; j < arrProviderList[i].length; j++){
if ((arrProviderList[i][j] == "") || (arrProviderList[i][j] == null)) {
break;
}
else {
arrAvengers.push(arrProviderList[i][j]); // place all Avengers listed for each provider inside the blank array
}
}
Logger.log(i + 1 + ') Provider: ' + arrProviderList[i][0] + '\nArray: ' + arrAvengers);
FormApp.openById(formID).getItemById("217536202").asListItem().setChoiceValues(arrAvengers); // update drop dwon with Avengers
Logger.log("https://docs.google.com/forms/d/e/1HvwvEaf3mCscVmYfrS4Zsn58pqznh8rvg4LCPLMBGMw/viewform?usp=pp_url&entry.217536202=" + arrAvengers + "&entry.192318046&entry.703889307&entry.201973220&entry.468746612&entry.2058453829&entry.1670526684&entry.73658514")
}
}
I tried populating the URL with parameters but that didn't work. I got the below error when accessing this link for Pepper Potts:
https://docs.google.com/forms/d/e/1HvwvEaf3mCscVmYfrS4Zsn58pqznh8rvg4LCPLMBGMw/viewform?usp=pp_url&entry.217536202=Tony+Stark&entry.192318046&entry.703889307&entry.201973220&entry.468746612&entry.2058453829&entry.1670526684&entry.73658514
I'm not sure where I went wrong nor how to resolve this? Has anyone managed to populate drop-down values with a similar use case to mine?
If Happy Hogan were to receive the feedback form then I'd want his drop-down values to display:
If Phil Coulson were to receive the feedback form then I'd want Groot's drop-down value to display:
I've also noticed that the IDs generated from my questionID() function isn't the same as the IDs found in the pre-filled link. Why is that? I thought they would be the same, no?
FYI - I'm unable to Inspect webpages on my Google Form on my company laptop in case someone suggests that!
Many thanks in advance!
What I am attempting to do is to use some script to automate locating then forward to a specific email without having to switch over to Gmail to manually forward this on, However I cannot use the auto forward built into Gmail due to only certain emails needing to be sent, There are also multiple emails with the same reference number in the subject field unless I add in the constant then there is only one,
The emails subject get sent to me in the following format a unique reference number followed by hyphen then the constant (123456 - Constant),
Currently I am attempting to use the built in Gmail search via reference number + Constant, The reference number is stored in a google sheets cell, I have then been tagging on the "constant" within the script.
However despite using "" which to my understanding is for an exact match, this is forwarding multiple entries with different reference numbers,
Currently the code I am attempting to use is
function Search2() {
var sheet = SpreadsheetApp.getActiveSheet(); //gets sheet
var row = 1;
var col = 8;
var REF = sheet.getRange(row, col).getValue(); //gets value of the unique reference number
var threads = GmailApp.search('subject:"REF"+"CONSTANT"') //adds in the constant for the search
for (var h = 0; h < threads.length; h++) {
var messages = threads[h].getMessages();
for (var i = 0; i < messages.length; i++) {
Logger.log(messages[i].getSubject());
messages[i].forward("EMAIL ADDRESS", {
cc: "",
bcc: ""
});
}
}
}
My current line of thought is that there must be something not working correctly when I combine the reference and the constant causing it to not look for an exact match and returning closest matches.
Any assistance would be appreciated,
Update 30/07/2021 # 18:46 - I did a bit of testing replacing the "REF" directly with the reference number works and returns 1 result forwarded, This leads my thought process to there is an issue with the REF variable pulling through the correct value.
GmailApp.search(`subject:"${REF}" subject:"CONSTANT"`);
GmailApp.search(`subject:"${REF} CONSTANT"`);
Reference:
Template literals
Just a guess. Try this:
var threads = GmailApp.search('subject:' + REF + CONSTANT)
(I suppose CONSTANT is a variable)
Look. Suppose this works:
var threads = GmailApp.search('subject:"123 - Abcd"');
This means that this should work as well:
var REF = 123;
var CONSTANT = " - Abcd";
var threads = GmailApp.search('subject:"' + REF + CONSTANT + '"');
There is nothing to be broken at all.
See with Logger.log() how exactly your variables REF, CONSTANT and the combination 'subject:"' + REF + CONSTANT + '"' look like.
I'm trying to read ALL email in my gmail account - inbox, sent, draft, trash, emails with labels, archive, etc. I could live without the junk but I want everything else.
(all examples below use try {} catch {} to avoid errors with empty labels etc.)
I've tried
for (var i=StartLabel; i<=EndLabel; i++)
{
var label = labels[i].getName();
// get all messages, then join them into a single dimension array
var messages = GmailApp.getMessagesForThreads(GmailApp.search("label:" + label))
.reduce(function(a, b) {return a.concat(b);});
CountByLabels += messages.length;
}
That gives me everything in the labels (I think) but not the other stuff.
I tried other things, to get the inbox (to combine with the above) or all of the emails
var messages = GmailApp.getMessagesForThreads(GmailApp.getInboxThreads()).reduce(function(a, b) {return a.concat(b);});
CountInbox += messages.length;
but I only get about 549 results (GMail shows 5,478). If I add in the results from getPriorityInboxThreads I get 1,829 results.
I tried
// get all messages, then join them into a single dimension array
var messages = GmailApp.getMessagesForThreads(GmailApp.search("(is:unread OR is:read) in:anywhere")).reduce(function(a, b) {return a.concat(b);});
CountByLabels += messages.length;
I get 598 results.
I tried different search terms in the code directly above, eg:
is:unread = 528 results
is:read = 1,037 results
is:read OR is:unread = 599 results
None of them gave the right number, or even close, and incidentally if I try those search terms directly in gmail I get a totally different, and much higher, result for each - several thousand, or 'many'.
I don't think this is related to How to use Google App Scripts to retrieve Gmail emails in a customised way? as the numbers returned are not round numbers (eg 500).
I'm assuming that I can use getSpamThreads, getStarredThreads, getTrashThreads, getDraftMessages to get the relevant folders but until I understand why I'm only getting some emails from the inbox I don't trust those to give me everything.
Can anyone help?
Try this:
function allEmailsInLabels() {
var allLabels,i,j,L,L2,msgCount,theCount,threads,thisLabel;
msgCount = 0;
theCount = 0;
allLabels = GmailApp.getUserLabels();
L = allLabels.length;
for (i = 0; i < L; i++) {
Logger.log("label: " + allLabels[i].getName());
thisLabel = allLabels[i];
threads = thisLabel.getThreads();
//Logger.log('threads: ' + threads);
L2 = threads.length;
for (j = 0; j < L2; j++) {
msgCount = threads[j].getMessageCount();
//Logger.log('thread message count: ' + threads[j].getMessageCount());
// You could do something with threads[j] here like
// threads[j].moveToTrash();
theCount = theCount + msgCount;
};
};
//Logger.log('theCount: ' + theCount);
};
It first gets all the labels, then the threads, then the message count in each thread, and keeps a running count. You'll also need to get the messages in the inbox, that code doesn't include them. This is the sample code from the documentation that shows the basic concept:
// Log the subject lines of your Inbox
var threads = GmailApp.getInboxThreads();
for (var i = 0; i < threads.length; i++) {
Logger.log(threads[i].getFirstMessageSubject());
}
I had the same question. Reading a little bit more in the reference in the Google Developers Website, I discovered, reading about the function moveToInbox, a Google sample that used the Search to get all e-mails that weren't in the Inbox (https://developers.google.com/apps-script/reference/gmail/gmail-thread#movetoinbox). I decided to combine this with the getInboxThreads and with these two, my code was shorter and found every e-mail that I had received (less spam and junk).
function getEmails() {
var generalThreads, inboxThreads;
inboxThreads = GmailApp.getInboxThreads();
generalThreads = GmailApp.search('-in:inbox');
}
Every single email that was in the folder "All mail" in the Gmail was in these two variables after this.
I don't know if this can help anyone, but surely helped me.
I know this is coming a bit delayed, but having had the same problem and looking at some of the solutions offered here, I wanted to offer up my own solution, which also uses the search function:
function getEmails() {
var allEmailThreads = GmailApp.search('label:all')
}
This actually filters for every email, regardless of the mailbox, and seems to me to be the simplest solution to the question.
This is not an answer to your problem (but is probably one of the reasons your total results returned don't agree with what you are seeing in gmail inbox) but highlights one of the problems I encountered when calling getPriorityInboxThreads() is that it ignores any thread that is not flagged as "important" in the primary inbox.
//returns 10 threads and 1st message for each thread
function getThreads(){
var ret = '';
var threads = GmailApp.getPriorityInboxThreads(0, 10);
for (var i = 0 ; i < threads.length; i++) {
var id = threads[i].getId();
var message = GmailApp.getMessageById(id);
ret += "subject: " + message.getSubject() +'\n';
Logger.log("subject: " + message.getSubject());
/*Edited this out as it doesn't return anything
//check for labels on this thread
var labels = threads[i].getLabels();
for (var j = 0; j < labels.length; j++) {
Logger.log(labels[j].getName());
} */
}
return ret;
}
"Important" is classed as a system flag and getPriorityInboxThreads() ignores any thread that is not flagged important....
I would like to select all threads in "Primary" inbox irrespective of being labelled as "important".
To test, simply change any thread in inbox to important or not etc.
After I published a video on how to get Gmail messages into a Google spreadsheet, I received a feedback from some viewers that they could only get a number of messages but others fail to be processed. Therefore, I did some research and found that the process of getting emails may fail and make the system unable to handle the huge amount of emails. This is mentioned in the Gmail API here:
https://developers.google.com/apps-script/reference/gmail/gmail-label#getthreads
The documentation suggests to use getThreads(start, max) where start and max are the limiting parameters.
You may view the video and download the full code from YouTube and GitHub:
https://youtu.be/gdgCVqtcIw4
I'm just starting to learn how to code with Google Apps Script. Barely.
I have a guild roster sheet (toon names in range B2:B and Realm (We also add prospects to this list) in range C2:C.
In Column D, I would like to set the value of each cell row-by-row to a function that uses the data in B and C (i.e. "=wowi(B2,C2)")
My intent is to just be able to add as many names as I want in Column B, and run the function manually to update the values for each in column D, and sleeps for a second between each (so as to not abuse the UrlFetch in the "=wowi(toonName, realmName)" function when the names column gets a little long)
Sadly, this is about as far as I got before I gave up:
function rosterUpdate() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destCell = sheet.getRange("D2");
for (x = 0, x //(less than or equal to)// sheet.getRange("B2:B").getNumRows(), x ++) {
if (x != 0 {
NOCLUEWHATTODO-IDKWHYIEVENTRY;
sleep(1000);
}
}
}
I know I need to use a for, but not quite sure on how to go about reading the value and modifying the D cell's value. I'm quite possibly overthinking this, as it's currently 5:30 in the morning and I'm running on about 2 hours of sleep, but would really appreciate the help.
Thanks in advance. Sorry if I'm bad. :[
Hopefully this answers your question in terms of getting and setting values (you should check the documentation: https://developers.google.com/apps-script/reference/spreadsheet/range) and how to sleep for one second between function calls, however it's pretty inefficient as it will iterate over the whole list each time.
Please be aware this is written off the top of my head very quickly so you may need to play with the i values to get it working properly, and there is probably a much, much cleaner way to do it!
var listLength = sheet.getLastRow() - 1;
for(var i = 0; i < listLength; i++){
var toonName = sheet.getRange("B" + (i+2)).getValue();
var realmName = sheet.getRange("C" + (i+2)).getValue();
sheet.getRange("D" + (i+2)).setValue(yourFunction(toonName, realmName));
Utilities.sleep(1000);
}