问题
I have a google sheet that has project data(project code, name, dates project leaders et.c) and I am trying to get row data for unique values in this case emails. If for example email johndoe@testmail.com appear x number of times, the row data should be captured and passed to html file then emailed to johndoe@testmail.com same case to other emails. So specific project leaders should receive emails on only the projects they lead. I have am newbie in JavaScript/GAs, and have done tonnes of research but could not get it to work. I will appreciate help with this.
Email script that reads the sheet
function Getdata(){
const ss= SpreadsheetApp.getActiveSpreadsheet();
const ws= ss.getSheetByName("Projects");
const headers= ws.getRange("A1:J").getValues();
const Project_Code = headers[0][0];
const End_Date = headers[0][1];
const Project_Name = headers[0][2];
const New_End_Date = headers[0][3];
const Contact_Finance = headers[0][5];
const Update_client = headers[0][6];
const Project_Leader = headers[0][7];
const Email = headers[0][11];
const lr = ws.getLastRow();
const tableRangeValues=ws.getRange(2,1, lr-1,9).getDisplayValues();
const htmlTemplate = HtmlService.createTemplateFromFile("Notify")
htmlTemplate.Project_Code = Project_Code;
htmlTemplate.End_Date= End_Date;
htmlTemplate.Project_Name= Project_Name;
htmlTemplate.New_End_Date=Complete;
htmlTemplate.Contact_Finance=Contact_Finance;
htmlTemplate.Update_client=Upadate_client;
htmlTemplate.Project_Leader=Project_Leader;
htmlTemplate.tableRangeValues=tableRangeValues;
const htmlForEmail = htmlTemplate.evaluate().getContent();
// This is the part where email loop should happen? and only send emails to individual project leaders listing only the projects they work on.
MailApp.sendEmail({
to: // emails based on loops?
subject: 'Projects Ending',
htmlBody: htmlForEmail,
//Logger.log(tableRangeValues);
});
}
回答1:
How to separate 1 array into multiple arrays by a key value
I suggest using Array.prototype.reduce to organize your array of rows into an object that has emails as keys and arrays of rows as values (example below). You'll end up with
{
"person1@email.com": [[value1],[value2],[value3]],
"person2@email.com": [[value1],[value2],[value3]],
// etc.
}
See below for a reducer that is specific to your needs, but in general the idea is
function reducer(object, arrayOfValues) {
const key = arrayOfValues[INDEX_OF_KEY];
if (!(key in object)) object[key] = [];
object[key].push(arrayOfValues);
return object;
}
const groupedByKey = valuesFromSheet.reduce(reducer, {});
You can setup a loop through an Object with a relatively recent addition to the global Object
: Object.entries()
. This will return an array of arrays so you can send your email with Array.prototype.forEach
(example below).
For each email, create a new template. You should take advantage of Apps Script's templating ability to modularize your templates. When you add all your styling in and then start adding scriplet tags <? ?>
it can become a real mess. I have demonstarted below how you can separate the table template from the main email template and end up with nested templates. This is much more manageable and debuggable/testable.
Finally, I also always recommend defining your headings in one place (see the HEADINGS
object below) so you can easily change up the heading order later without updating the code in multiple places (an easy way to introduce a bug into your script).
Code.gs
const HEADINGS = {
PROJECT_CODE: 0,
END_DATE: 1,
PROJECT_NAME: 2,
NEW_END_DATE: 3,
CONTACT_FINANCE: 5,
UPDATE_CLIENT: 6,
PROJECT_LEADER: 7,
LAST_NAME: 8,
FULL_NAME: 9,
EMAIL: 11
};
function Getdata(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ws = ss.getSheetByName("Projects");
const headers = makeHeaders(ws.getRange("A1:J").getValues()[0]);
const lastRow = ws.getLastRow();
const tableRangeValues = ws
.getRange(2, 1, lastRow - 1, HEADINGS.EMAIL + 1)
.getDisplayValues();
const groupedByEmail = tableRangeValues.reduce(emailReducer, {});
Object.entries(groupedByEmail).forEach(function(emailGroup) {
const [email, values] = emailGroup;
MailApp.sendEmail({
to: email,
subject: "Projects Ending",
htmlBody: makeHtmlEmail(values[0][HEADINGS.FULL_NAME], headers, values)
});
});
}
function emailReducer(groups, row) {
const email = row[HEADINGS.EMAIL];
if (!(email in groups)) groups[email] = [];
groups[email].push(row.slice(HEADINGS.PROJECT_CODE, HEADINGS.FULL_NAME + 1));
return groups;
}
function makeHtmlEmail(name, header, values) {
const emailTemplate = HtmlService.createTemplateFromFile("Notify");
emailTemplate.name = name;
emailTemplate.header = header;
emailTemplate.values = values;
return emailTemplate.evaluate().getContent();
}
function makeTable(header, values) {
const table = HtmlService.createTemplateFromFile("table");
table.header = header;
table.values = values;
return table.evaluate().getContent();
}
function makeHeaders(headerRow) {
return [
headerRow[HEADINGS.PROJECT_CODE],
headerRow[HEADINGS.END_DATE],
headerRow[HEADINGS.PROJECT_NAME],
headerRow[HEADINGS.NEW_END_DATE],
headerRow[HEADINGS.CONTACT_FINANCE],
headerRow[HEADINGS.UPDATE_CLIENT],
headerRow[HEADINGS.PROJECT_LEADER]
];
}
Notify.html
<body>
<head><!-- style, etc... omitted for clarity --></head>
<body>
<p>Hi <?= name ?></p>
<!-- details omitted for clarity -->
<h4> Project Details </h4>
<?!= makeTable(header, values) ?>
</body>
</body>
table.html
<!-- styling omitted for clarity -->
<table>
<thead>
<tr>
<? header.forEach(heading => { ?>
<th><?= heading ?></th>
<? }) ?>
</tr>
</thead>
<tbody>
<? values.forEach(row => { ?>
<tr>
<? row.forEach(value => { ?>
<td><?= value ?></td>
<? }); ?>
</tr>
<? }); ?>
</tbody>
</table>
来源:https://stackoverflow.com/questions/62866209/how-do-i-pass-unique-values-in-google-sheet-to-html-email-template