【问题标题】:Finding all checkboxes in a Google Sheet查找 Google 表格中的所有复选框
【发布时间】:2019-02-28 22:26:25
【问题描述】:

自从 2018 年将复选框添加到原生 Google 表格 UI 以来,开发人员一直希望以编程方式读取它们或以某种方式处理它们,例如将它们视为“单选按钮”、将它们重置为“未选中”或设置他们要“检查”。

我们如何才能最好地在给定的 Google 表格中找到复选框,从而避免在处理其他单元格的状态时意外修改它们?

一种方法是检查工作表上的值并将任何true/false 值视为复选框:

function getAllCheckboxes() {
  const wb = SpreadsheetApp.getActive();
  const checkboxes = [];

  wb.getSheets().forEach(function (sheet) {
    var rg = sheet.getDataRange();
    var values = rg.getValues();
    var sheetCheckBoxes = [];

    values.forEach(function (row, r) {
      row.forEach(function (val, c) {
        // Checkbox values are stored as `false` (unchecked) and `true` (checked)
        if (val === false || val === true) {
          sheetCheckBoxes.push({
            rowIndex: r,
            colIndex: c,
            value: val,
            r1c1: "R" + (r+1) + "C" + (c+1)
          });
        }
      });
    });
    if (sheetCheckBoxes.length) {
      checkboxes.push({
        name: sheet.getName(),
        sheetId: sheet.getSheetId(),
        boxes: sheetCheckBoxes
      });
    }
  });

  return checkboxes; // An array of objects describing a sheet and its checkboxes.
}

但是,这不适用于所有用例:单元格可能显示为文字 TRUEFALSE,而不是复选框。上面的代码将把它当作一个,因为它共享相同的值。

【问题讨论】:

    标签: checkbox google-apps-script google-sheets google-sheets-api


    【解决方案1】:

    复选框在 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;
    }
    

    有效地使用复选框位置

    一旦知道电子表格中的哪些单元格对应于复选框 - 以及哪些值显示为“选中”与“未选中” - 很自然地想要读取或修改它们。盲目地将 truefalse 写入复选框单元格仅对默认(布尔)复选框有效。要处理所有可能的用户创建的复选框,您必须编写适当的值,表示“选中”或“未选中”。(上述脚本将这些值存储在 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);
    }
    

    【讨论】:

      【解决方案2】:

      这似乎有效:

      function testForCheckBoxes() {
        var ss=SpreadsheetApp.getActive();
        var sh=ss.getActiveSheet();
        var rg=sh.getDataRange();
        var vA=rg.getDataValidations();
        var cbA=[];
        for(var i=0;i<vA.length;i++) {
          for(var j=0;j<vA[i].length;j++) {
            var rule=vA[i][j];
            if(rule!=null) {
              var criteria = rule.getCriteriaType();
              if(criteria == SpreadsheetApp.DataValidationCriteria.CHECKBOX) {
                cbA.push(Utilities.formatString('%s', sh.getRange(i+1,j+1).getA1Notation()));
              }
            }
          }
        }
        var ui=HtmlService.createHtmlOutput(cbA.join(', '));
        SpreadsheetApp.getUi().showModelessDialog(ui, 'Check Boxes in A1Notation')
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-27
        • 2014-12-08
        • 2022-11-02
        • 1970-01-01
        • 2012-01-17
        • 2011-06-04
        • 2011-03-25
        • 1970-01-01
        相关资源
        最近更新 更多