具有合并下拉菜单的Google Spreadsheet动态条件格式

时间:2018-10-08 17:06:44

标签: google-sheets gs-conditional-formatting

我的工作表的工作方式

我正在制作电子表格以显示我有多少零件。通过使用下拉菜单,我可以证明我创建了一个产品。使用条件格式,我将显示在创建产品时,拥有0个商品并不是问题。创建的商品有0个项目,从红色变为紫色。紫色表示此产品中有0件物品无关紧要。

我的问题

我的问题始于我的下拉菜单。如果合并单元格,则该值将进入左上角的单元格。这意味着合并后的单元格内的其他单元格为空白。这给我带来了条件格式的问题。

我的条件格式代码示例:

=if($D2=0;$E2="Created")

由于条件和下拉列表的原因,我必须为每个单元格更改此代码。拥有250行以上的行将非常困难。

我的问题

  1. 有没有办法以一种有效的方式为合并后的单元格的所有单元格赋予合并后的单元格的值?
  2. 是否有更好的方法使我的条件格式代码适用于合并的单元格?

这是我的工作表

Product items collected sheet link(显示问题和解决方案!)

Product items collected sheet image (Version 1)

Product items collected sheet image (Version 2)

1 个答案:

答案 0 :(得分:1)

这个问题的核心是合并单元的操作。合并某个单元格时(例如,在几行中),只有合并后的单元格左上方的单元格才能包含数据,对条件格式进行响应等。从某种意义上说,其他单元格不复存在,无法为它们分配值。

发问者问:
问:是否有办法有效地为合并单元格的所有单元格提供合并单元格的值?
答:不。不仅仅是以“有效”的方式。只是不可能。

问:是否有更好的方法使我的条件格式代码适用于合并的单元格?
答:不,是;)
否。就合并单元格而言,一切都由合并范围顶部单元格中的值决定。合并单元格的“其余”没有其他选择。
是。我将在此屏幕快照中的F列中创建一个“帮助程序”单元格


Helper Column before and after


实现此目的的代码是动态的-它会自动适应于添加更多产品,更多项目等。

逻辑很简单:从F2开始,测试E2是否具有值(也就是说,它是否是合并单元格的顶部?)。如果是,则将E2的值分配给F2并将该值放入以下单元格的变量中。如果不是,则E列中的单元格必须是合并的单元格的一部分,因此将F列的值分配给之前保存的变量。


function so5270705902() {

    // basic declarations
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    // note this is going to work on the second sheet in the spreadsheet - this can be edited.
    var sheet = ss.getSheets()[1];

    // Column B contains no merged cells, and always contains data (it is the BOM for the Products).
    // so we'll use it to established the last row of data.
    var Bvals = sheet.getRange("B1:B").getValues();
    var Blast = Bvals.filter(String).length;

    // Row 1 is a header row, so data commences in Row 2 - this can be edited
    var dataStart = 2;
    // Logger.log("the last row in column D = "+Blast);// DEBUG


    // set up to loop through the rows of Column F
    var mergedcellvalue = "";

    for (i = dataStart; i < (Blast + 1); i++) {

        // set the range for the row
        var range = sheet.getRange(i, 6);
        //Logger.log("row#"+i+" = "+range.getA1Notation()); DEBUG


        // get the value in column E
        var ECell = range.offset(0, -1);
        var ECellVal = ECell.getValue();
        //Logger.log("offsetrange#"+i+" range value = "+ECellVal);
        //Logger.log("Column E, row#"+i+", value = "+ECell.getA1Notation()+" range value = "+ECellVal);//DEBUG

        // when a row is merged, on the top row contains any data
        // so we'll evaluate to see whether there is any value in this row in Column E
        if (ECell.isBlank()) {

            //Logger.log("ECell is blank. We're in the middle of the Merged Cell"); ??DEBUG

            // Set the value to the lastes value of "mergedcellvalue"
            range.setValue(mergedcellvalue);

        } else {

            //Logger.log("ECell has a value. We're at the top of the merged cell");//DEBUG

            // paste the ECellVal into this range
            range.setValue(ECellVal);

            // Update the "mergedcellvalue" variable so that it can be applied against lower cells of this merged cell
            mergedcellvalue = ECellVal;

        } // end of the if isblank

    } // end of the loop through column F

}

更新2018年10月22日

出于开发目的,我在E列中仅使用了14行的一小部分。但是,发问者的数据涵盖了250行以上,因此我将开发测试扩展到了336行(是的,我知道,但是我正在复制/粘贴)我最终得到了336,并且懒得删除任何行(好吗?)。我发现该代码花费了81秒以上的时间来处理。不好。

处理时间长的主要原因(大约80秒)是因为循环中存在getValue语句-var ECellVal = ECell.getValue();。每个实例大约花费0.2秒。在循环中包含getValue是典型的性能错误。我的错。因此,我修改了代码以获取列E的值之前循环
var Evals = sheet.getRange("e2:E").getValues();

当执行时间保持在同一水平时,我感到很惊讶。原因是isBlank评估-if (ECell.isBlank()) {以前完全没有时间,现在每个实例消耗@ 0.2秒。不好++。因此,在搜索Stack Overflow之后,我将这一行修改如下:
if (!Evals[(i-dataStart)][0]) {

在循环中包含setValues也会带来麻烦。一种选择是将值写入数组,然后在循环后,使用数组更新列E值。但是,在这种情况下,执行时间似乎没有受到影响,我将setValues留在了循环中。

通过这两个更改,现在总执行时间为1.158秒。这是很多的百分比降低。


function so5270705903() {

    // basic declarations
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    // note this is going to work on the second sheet in the spreadsheet - this can be edited.
    var sheet = ss.getSheets()[2];

    // Column B contains no merged cells, and always contains data (it is the BOM for the Products).
    // so we'll use it to established the last row of data.
    var Bvals = sheet.getRange("B1:B").getValues();
    var Blast = Bvals.filter(String).length;

    // Row 1 is a header row, so data commences in Row 2 - this can be edited
    var dataStart = 2;
    // Logger.log("the last row in column D = "+Blast);// DEBUG


    // set up to loop through the rows of Column F
    var mergedcellvalue = "";

    // get the values for Column E BEFORE the loop
    var Evals = sheet.getRange("e2:E").getValues();

    for (i = dataStart; i < (Blast + 1); i++) {



        // set the range for the row
        var range = sheet.getRange(i, 6);
        //Logger.log("row#"+i+" = "+range.getA1Notation()); DEBUG


        // get the value in column E
        var ECell = range.offset(0, -1);

        var ECellVal = Evals[(i - dataStart)][0];

        //Logger.log("Column E, row#"+i+", value = "+ECell.getA1Notation()+" range value = "+ECellVal);//DEBU

        // when a row is merged, on the top row contains any data
        // so we'll evaluate to see whether there is any value in this row in Column E
        // instead is isblank, which was talking 0.2 seconds to evaluate, this if is more simple
        if (!Evals[(i - dataStart)][0]) {

            //Logger.log("ECell is blank. We're in the middle of the Merged Cell"); //DEBUG

            // Set the value to the lastes value of "mergedcellvalue"
            range.setValue(mergedcellvalue);

        } else {

            //Logger.log("ECell has a value. We're at the top of the merged cell");//DEBUG

            // paste the ECellVal into this range
            range.setValue(ECellVal);

            // Update the "mergedcellvalue" variable so that it can be applied against lower cells of this merged cell
            mergedcellvalue = ECellVal;

        } // end of the if isblank

    } // end of the loop through column F

}

更新2019年3月3日

提问者对代码进行了最后更改。这段代码是最终的解决方案。


function reloadCreatedCells() {

  // Basic declarations.
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  // Note this is going to work on the second sheet in the spreadsheet - this can be edited.
  var sheet = ss.getSheets()[1];

  // Column B contains no merged cells, and always contains data (it is the BOM for the Products).
  // so we'll use it to established the last row of data.
  var D_vals = sheet.getRange("D1:D").getValues();
  var D_last = D_vals.filter(String).length;

  // First row with data.
  var dataStart = 2;

  // Set up to loop through the rows of Column H - K.
  var mergedcellvalue = "";

  // Get the values for Column H - K BEFORE the loop.
  var H_K_vals = sheet.getRange("H2:K").getValues();

  // How many people we have.
  var people = 4;

  // The first vertical row.
  var rowStart = 12;

  // Horizontal rows.
  for (var h = 0; h < people; h++) {

    // Vertical rows.
    for (var v = dataStart; v < D_last; v++) {

      // Set the range for the row.
      var range = sheet.getRange(v, rowStart + h);
      // Logger.log(range.getA1Notation()); //DEBUG

      // Get the value in column H - K.
      var H_K_Cell = range.offset(0, -people);

      // Adding Created and not created values inside L - O.
      var H_K_CellVal = H_K_vals[(v - dataStart)][h];
      // Logger.log(H_K_Cell.getA1Notation() + ': ' + H_K_CellVal); //DEBUG

      // When a row is merged, the value is only inside the top row.
      // Therefore, you need to check if the value is empty or not.
      // If the value is empty. Place the top value of the merged cell inside the empty cell.
      if (!H_K_vals[(v - dataStart)][h]) {
        // Logger.log(H_K_Cell.getA1Notation() + ": is blank. We're below the top cell of the merged cell."); //DEBUG

        // Set the value to the top cell of the merged cell with "mergedcellvalue".
        range.setValue(mergedcellvalue);

      } else {
        // Logger.log(H_K_Cell.getA1Notation() + ": has a value. We're at the top of the merged cell."); //DEBUG

        // Paste the H_K_CellVal into this range.
        range.setValue(H_K_CellVal);

        // Update the "mergedcellvalue" variable, so that it can be applied against lower cells of this merged cell.
        mergedcellvalue = H_K_CellVal;

      } // end of the if isblank.

    } // End of the vertical row loop.
  } // End of the horizontal row loop.
}