【发布时间】:2021-06-09 06:37:02
【问题描述】:
我在此处查看了 Google 提供的以下有关如何优化现有 Google 脚本的文档: https://developers.google.com/apps-script/guides/support/best-practices
特别是,“使用批处理操作”部分似乎更适合我的用例,其中最佳策略是将所有读取“批处理”到一个操作中,然后在单独的操作中写入;不要在读写调用之间循环。
这是一个效率低下的代码示例,如上面的 url 所示:
// DO NOT USE THIS CODE. It is an example of SLOW, INEFFICIENT code.
// FOR DEMONSTRATION ONLY
var cell = sheet.getRange('a1');
for (var y = 0; y < 100; y++) {
xcoord = xmin;
for (var x = 0; x < 100; x++) {
var c = getColorFromCoordinates(xcoord, ycoord);
cell.offset(y, x).setBackgroundColor(c);
xcoord += xincrement;
}
ycoord -= yincrement;
SpreadsheetApp.flush();
}
这是一个高效和改进的代码示例:
// OKAY TO USE THIS EXAMPLE or code based on it.
var cell = sheet.getRange('a1');
var colors = new Array(100);
for (var y = 0; y < 100; y++) {
xcoord = xmin;
colors[y] = new Array(100);
for (var x = 0; x < 100; x++) {
colors[y][x] = getColorFromCoordinates(xcoord, ycoord);
xcoord += xincrement;
}
ycoord -= yincrement;
}
sheet.getRange(1, 1, 100, 100).setBackgroundColors(colors);
现在,对于我的特定用例:
我不想将值存储在数组中,然后将它们写入/修改它们作为与将它们读入数组的单独操作,而是创建多个 Google 文档来替换每个文档中的占位符。
对于上下文:
我正在编写一个脚本,该脚本读取学生电子表格,其中包含要为每个学生修改的文件,稍后将作为邮件合并发送。例如,有 3 个主文件。每个学生将拥有 3 个主文件的副本,用于.replaceText 占位符字段。
下面是我相关的sn-ps代码:
function filesAndEmails() {
// Import the Spreadsheet application library.
const UI = SpreadsheetApp.getUi();
// Try calling the functions below; catch any error messages that occur to display as alert window.
try {
// Prompt and record user's email draft template.
// var emailLinkID = connectDocument(
// UI,
// title="Step 1/2: Google Document (Email Draft) Connection",
// dialog=`What email draft template are you referring to?
// This file should contain the subject line, name and body.
// Copy and paste the direct URL link to the Google Docs:`,
// isFile=true
// );
// TEST
var emailLinkID = "REMOVED FOR PRIVACY";
if (emailLinkID != -1) {
// Prompt and record user's desired folder location to store generated files.
// var fldrID = connectDocument(
// UI,
// title="Step 2/2: Google Folder (Storage) Connection",
// dialog=`Which folder would you like all the generated file(s) to be stored at?
// Copy and paste the direct URL link to the Google folder:`,
// isFile=false
// );
// TEST
var fldrID = DriveApp.getFolderById("REMOVED FOR PRIVACY");
// Retrieve data set from database.
var sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_1);
// Range of data must include header row for proper key mapping.
var arrayOfStudentObj = objectify(sheet.getRange(3, 1, sheet.getLastRow()-2, 11).getValues());
// Establish array of attachment objects for filename and file url.
var arrayOfAttachObj = getAttachments();
// Opportunities for optimization begins here.
// Iterate through array of student Objects to extract each mapped key values for Google document insertion and emailing.
// Time Complexity: O(n^3)
arrayOfStudentObj.forEach(function(student) {
if (student[EMAIL_SENT_COL] == '') {
try {
arrayOfAttachObj.forEach(function(attachment) {
// All generated files will contain this filename format, followed by the attachment filename/description.
var filename = `${student[RYE_ID_COL]} ${student[FNAME_COL]} ${student[LNAME_COL]} ${attachment[ATTACH_FILENAME_COL]}`;
// Create a copy of the current iteration/file for the given student.
var file = DocumentApp.openById(DriveApp.getFileById(getID(attachment[ATTACH_FILEURL_COL], isFile=false)).makeCopy(filename, fldrID).getId())
// Replace and save all custom fields for the given student at this current iteration/file.
replaceCustomFields(file, student);
});
} catch(e) {
}
}
});
UI.alert("Script successfully completed!");
};
} catch(e) {
UI.alert("Error Detected", e.message + "\n\nContact a developer for help.", UI.ButtonSet.OK);
};
}
/**
* Replaces all fields specified by 'attributesArray' given student's file.
* @param {Object} file A single file object used to replace all custom fields with.
* @param {Object} student A single student object that contains all custom field attributes.
*/
function replaceCustomFields(file, student) {
// Iterate through each student's attribute (first name, last name, etc.) to change each field.
attributesArray.forEach(function(attribute) {
file.getBody()
.replaceText(attribute, student[attribute]);
});
// Must save and close file to finalize changes prior to moving onto next student object.
file.saveAndClose();
}
/**
* Processes the attachments sheet for filename and file ID.
* @return {Array} An array of attachment file objects.
*/
function getAttachments() {
var files = SpreadsheetApp.getActive().getSheetByName(SHEET_2);
return objectify(files.getRange(1, 1, files.getLastRow(), 2).getValues());
}
/**
* Creates student objects to contain the object attributes for each student based on
* the header row.
* @param {Array} array A 2D heterogeneous array includes the header row for attribute key mapping.
* @return {Array} An array of student objects.
*/
function objectify(array) {
var keys = array.shift();
var objects = array.map(function (values) {
return keys.reduce(function (o, k, i) {
o[k] = values[i];
return o;
}, {});
});
return objects;
}
总结一下我的代码,我将学生的 Google 电子表格读取为对象数组,因此每个学生都有他们的名字、姓氏、电子邮件等属性。我对文件附件做了同样的事情包括每个学生。目前,forEach 循环遍历每个学生对象,创建主文件的副本,替换每个文件中的占位符文本,然后将它们保存在文件夹中。最终,我将使用MailApp 将这些文件发送给每个学生。但是,由于通过为每个学生创建文件副本进行重复的外部调用,执行时间非常慢是可以理解的......
TLDR
当我的用例需要多个 DriveApp 调用来创建文件的所述副本以进行修改时,是否仍然可以使用“批处理操作”优化我的代码?与将原始值读入数组并在以后的操作中修改它们相反,我认为我不能简单地将文档对象存储到数组中,然后在稍后阶段修改它们。想法?
【问题讨论】:
标签: arrays google-apps-script google-sheets optimization mailmerge