我的工作表如何运作
我正在制作一个电子表格来显示我有多少部分。通过使用下拉列表,我能够证明我创建了一个产品。使用条件格式,我表明在创建产品时,有0个项目不是问题。具有0项的创建产品从红色变为紫色。紫色意味着从该产品中获得0件物品无关紧要。
我的问题
我的问题始于我的下拉菜单。如果我合并单元格,该值将进入左上角单元格。这意味着合并单元格内的其他单元格为空白。这给了我条件格式的问题。
我的条件格式代码示例:
=if($D2=0;$E2="Created")
我必须为每个单元格更改此代码,因为条件与下拉列表相结合。拥有超过250行将是非常难以手工完成的。
我的问题
这是我的表
Product items collected sheet link(显示问题和解决方案!)
这个问题的核心是合并细胞的运作。合并单元格时,例如多行,只有合并单元格左上角的单元格可以包含数据,响应条件格式,等等。从某种意义上说,其他细胞不再存在,并且不能将值分配给它们。
提问者问: 问:有没有办法以合理的方式为合并单元格的所有单元格提供组合单元格的值? 答:不。不只是以“有效”的方式;这是不可能的。
问:有没有更好的方法使条件格式代码适用于合并单元格? 答:不,是的;) 不会。就合并单元而言,一切都是由合并范围的顶部单元格中的值驱动的。合并单元格的“其余”没有其他选项。 是。我将在F列中创建一个“帮助”单元格,如此截图中所示
实现这一目标的代码是动态的 - 它将自动适应添加更多产品,更多项目等。
逻辑很简单:从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
}
出于开发目的,我在列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
}
提问者对代码做了最后的修改。此代码是最终解决方案。
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.
}