复选框在 Google 表格应用程序中作为数据验证的 a specific type 实现,并且可以具有用户指定的“已选中”和“未选中”值——不仅仅是 true 和 @ 987654332@。因此,要正确找到 only 复选框单元格,我们必须将 check the data validation type 应用于每个单元格。这可以通过两种方式在 Google Apps 脚本中完成:使用电子表格服务或使用 Google Sheets API (v4)。
电子表格服务
电子表格服务方法不需要您启用任何额外的符号标识符或启用 Google Cloud Platform 中的任何 API。但是,在某些情况下,它可能不如 Sheets API 快。
脚本与问题中的脚本非常相似,不同之处在于我们必须迭代二维数据验证规则数组,而不是值数组。 (如果我们不需要复选框的当前值,我们可以跳过获取values数组。)
function getAllCheckboxesViaService() {
const wb = SpreadsheetApp.getActive();
const checkboxes = [];
// The specific type of Data Validation that demarcates a UI checkbox.
const CB = SpreadsheetApp.DataValidationCriteria.CHECKBOX;
wb.getSheets().forEach(function (sheet) {
var rg = sheet.getDataRange();
var values = rg.getValues();
var sheetCheckBoxes = [];
var dvRules = rg.getDataValidations();
dvRules.forEach(function (row, r) { // iterate data validations instead of values
row.forEach(function (rule, c) {
if (rule && rule.getCriteriaType() === CB) {
sheetCheckBoxes.push({
rowIndex: r,
colIndex: c,
r1c1: "R" + (r+1) + "C" + (c+1),
choices: (rule.getCriteriaValues().length ? rule.getCriteriaValues() : [true, false]),
value: values[r][c],
});
}
});
});
if (sheetCheckBoxes.length) {
checkboxes.push({
name: sheet.getName(),
sheetId: sheet.getSheetId(),
boxes: sheetCheckBoxes
});
}
});
return checkboxes;
}
表格 API
要使用 Sheets API,必须首先在应用程序的 Google Cloud Platform 项目中启用它。对于 Apps 脚本项目,其中之一是自动创建的,并且可以从 Apps 脚本编辑器的“资源”菜单中访问。如果您不确定如何激活 Sheets 符号和表格 REST API,请查看 the Advanced Services guide。
当fields 部分响应掩码包括sheets/data/rowData/values/dataValidation 时,通过端点spreadsheets.get 检索数据验证。通常只是这个特定字段没有用——知道相关工作表的标题、ID 以及复选框的值也很有用,因此更有用的fields 规范是sheets(data(rowData(values(dataValidation,effectiveValue/boolValue))),properties(sheetId,title))。 (您可以在Google APIs Explorer 中试验有效的字段掩码)
Sheets API 中的相关数据验证类型为 BOOLEAN。我们可以通过 API 查询我们想要的电子表格一次,然后在本地检查生成的响应数据以确定哪些单元格有复选框,哪些没有:
function getAllCheckboxesViaAPI() {
const wbId = SpreadsheetApp.getActive().getId();
const fields = "sheets(data/rowData/values("
+ "dataValidation(condition(type,values/userEnteredValue)),"
+ "effectiveValue(boolValue,numberValue,stringValue)),"
+ "properties(sheetId,title))";
const resp = Sheets.Spreadsheets.get(wbId, {fields: fields}); // Enable before use...
if (!resp.sheets || !resp.sheets.length)
return [];
const checkboxes = [];
resp.sheets.forEach(function (sheetObj) {
if (!sheetObj.data || !sheetObj.data.length)
return;
var sheetCheckBoxes = [];
sheetObj.data.forEach(function (gridRange) {
gridRange.rowData.forEach(function (row, r) {
row.values.forEach(function (cell, c) {
if (cell.dataValidation && cell.dataValidation.condition
// Require the cell to be displayed as a Checkbox.
&& cell.dataValidation.condition.type === "BOOLEAN")
{
sheetCheckBoxes.push({
rowIndex: r,
colIndex: c,
r1c1: "R" + (r+1) + "C" + (c+1),
choices: (cell.dataValidation.condition.values ?
cell.dataValidation.condition.values : [true, false]),
value: cell.effectiveValue // object, e.g. {booleanValue: false} or {stringValue: "Yes"}
});
}
});
});
});
checkboxes.push({
name: sheetObj.properties.title,
sheetId: sheetObj.properties.sheetId,
boxes: sheetCheckBoxes
});
});
return checkboxes;
}
有效地使用复选框位置
一旦知道电子表格中的哪些单元格对应于复选框 - 以及哪些值显示为“选中”与“未选中” - 很自然地想要读取或修改它们。盲目地将 true 或 false 写入复选框单元格仅对默认(布尔)复选框有效。要处理所有可能的用户创建的复选框,您必须编写适当的值,表示“选中”或“未选中”。(上述脚本将这些值存储在 choices 属性中。 )
使用 RangeList 类在 Apps 脚本中最容易重置值,尽管 Sheets API 端点 spreadsheets.values.batchUpdate 可以达到类似的结果(是的,R1C1 表示法对于 Sheets API ValueRange 规范是可以接受的),尽管使用一些样板来构建请求。 API 方法能够发出单个请求,而电子表格服务只能实例化一个 RangeList 每个工作表(您需要为每种类型的复选框创建 1 个 RangeList,以避免写入不正确的值(例如 @ 987654351@ 当“未选中”值应为"No"))。
function getSpecificCBType(checkboxData, checkedVal, uncheckedVal) {
const desiredCBs = checkboxData.filter(function (sheetObj) {
return sheetObj.boxes.some(function (checkbox) {
return checkbox.choices[0] === checkedVal && checkbox.choices[1] === uncheckedVal;
});
}).reduce(function (acc, sheetObj) {
var desiredSheetCBs = sheetObj.boxes.filter(function (checkbox) {
return checkbox.choices[0] === checkedVal && checkbox.choices[1] === uncheckedVal;
});
if (desiredSheetCBs.length) {
acc.push({
name: sheetObj.name,
sheetId: sheetObj.sheetId,
boxes: desiredSheetCBs
});
}
return acc;
}, []);
return desiredCBs;
}
function resetSomeCBsViaService() {
const allCBs = /* method from above */;
const checkedValue = true;
const uncheckedValue = false;
const someCBs = getSpecificCBType(allCBs, checkedValue, uncheckedValue);
const wb = SpreadsheetApp.getActive();
// Set to checked, using a RangeList (could use Sheets API values#batchUpdate).
someCBs.forEach(function (sheetObj) {
wb.getSheetByName(sheetObj.name)
.getRangeList(sheetObj.boxes.map(function (checkbox) { return checkbox.r1c1; }))
.setValue(checkedValue);
});
}
要从 someCBs 构建 API 请求,类似这样就足够了:
function resetSomeCBsViaAPI() {
const allCBs = /* method from above */;
const checkedValue = true;
const uncheckedValue = false;
const someCBs = getSpecificCBType(allCBs, checkedValue, uncheckedValue);
const wbId = SpreadsheetApp.getActive().getId();
const rq = someCBs.reduce(function (rqb, sheetObj) {
var valueRanges = sheetObj.boxes.map(function (checkbox) {
return {
range: "'" + sheetObj.name + "'!" + checkbox.r1c1,
values: [ [checkedValue] ]
};
});
Array.prototype.push.apply(rqb.data, valueRanges);
return rqb;
}, {valueInputOption: "USER_ENTERED", data: []});
Sheets.Spreadsheets.Values.batchUpdate(rq, wbId);
}