Поиск всех флажков в Google Sheet - PullRequest
0 голосов
/ 01 марта 2019

С тех пор, как флажки были добавлены к собственному интерфейсу Google Sheets в 2018 году, разработчики хотели программно читать их или обрабатывать их определенным образом, например, обрабатывая их как «переключатели», сбрасывая их на «непроверенные» или устанавливаяих «проверил».

Как лучше всего найти флажки в данном Google Sheet, чтобы избежать случайного изменения других ячеек при манипулировании их состоянием?

Один из способов - это проверить значения на рабочем листе и обработать любые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.
}

Однако это не будет работать во всех случаях использования: ячейка может отображаться как литерал TRUE или FALSE, а некак флажок.Приведенный выше код будет обрабатывать его так, как если бы он был одним, поскольку он имеет то же значение.

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Это похоже на работу:

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')
}
0 голосов
/ 01 марта 2019

Флажки реализованы в приложении Google Sheets как определенный тип проверки данных, а может иметь указанные пользователем значения для "отмеченных" и "непроверенных" - не толькоtrue и false.Таким образом, чтобы правильно найти только ячеек, которые являются флажками, мы должны проверить тип проверки данных , который применяется к каждой ячейке.Это можно сделать в Google Apps Script двумя способами: с помощью службы электронных таблиц или с помощью Google Sheets API (v4).

служба электронных таблиц

Метод службы электронных таблиц не требует от васвключить любые дополнительные идентификаторы символов или включить любые API в Google Cloud Platform.Однако в некоторых случаях он может быть не таким быстрым, как API Sheets.

Сценарий очень похож на сценарий в вопросе, с той разницей, что мы должны повторять массив правил проверки двумерных данных, а немассив значений.(Если нам не нужно текущее значение флажка, мы можем пропустить получение массива 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 листов

Чтобы использовать API листов, он долженСначала включите его в проекте Google Cloud Platform приложения.Для проектов скриптов приложений один из них создается автоматически и доступен из меню «Ресурсы» в редакторе скриптов приложений.Просмотрите Руководство по расширенным услугам , если вы не знаете, как активировать символ Sheets и API REST Sheets.

Проверка данных извлекается через конечную точку spreadsheets.get, когда маска частичного ответа fields включает sheets/data/rowData/values/dataValidation.Обычно это конкретное поле бесполезно - также полезно знать заголовок, идентификатор и, возможно, значение флажка соответствующего листа, поэтому более полезной спецификацией fields является sheets(data(rowData(values(dataValidation,effectiveValue/boolValue))),properties(sheetId,title)).(Вы можете поэкспериментировать с действительными масками полей в Google API Explorer )

Соответствующий тип проверки данных в 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.)

Сброс значений проще всего выполнить в скрипте Apps с помощью класса RangeList, хотя конечная точка Sheets API spreadsheets.values.batchUpdate может достичь аналогичных результатов (да, нотация R1C1приемлемо для спецификации API Sheets ValueRange), хотя и с некоторыми образцами для построенияпросьбаПодход API может выдавать один запрос, в то время как служба электронных таблиц может создавать только один RangeList на лист (и вам нужно будет создать 1 RangeList для каждого типа флажка, чтобы избежать записи неправильных значений (например, false когда значение "unchecked" должно быть "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);
  });
}

Чтобы построить запрос API из someCBs, достаточно примерно такого:

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);
}
...