我的工作表的工作方式
我正在制作电子表格以显示我有多少零件。通过使用下拉菜单,我可以证明我创建了一个产品。使用条件格式,我将显示在创建产品时,拥有0个商品并不是问题。创建的商品有0个项目,从红色变为紫色。紫色表示此产品中有0件物品无关紧要。
我的问题
我的问题始于我的下拉菜单。如果合并单元格,则该值将进入左上角的单元格。这意味着合并后的单元格内的其他单元格为空白。这给我带来了条件格式的问题。
我的条件格式代码示例:
=if($D2=0;$E2="Created")
由于条件和下拉列表的原因,我必须为每个单元格更改此代码。拥有250行以上的行将非常困难。
我的问题
这是我的工作表
Product items collected sheet link(显示问题和解决方案!)
答案 0 :(得分:1)
这个问题的核心是合并单元的操作。合并某个单元格时(例如,在几行中),只有合并后的单元格左上方的单元格才能包含数据,对条件格式进行响应等。从某种意义上说,其他单元格不复存在,无法为它们分配值。
发问者问:
问:是否有办法有效地为合并单元格的所有单元格提供合并单元格的值?
答:不。不仅仅是以“有效”的方式。只是不可能。
问:是否有更好的方法使我的条件格式代码适用于合并的单元格?
答:不,是;)
否。就合并单元格而言,一切都由合并范围顶部单元格中的值决定。合并单元格的“其余”没有其他选择。
是。我将在此屏幕快照中的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.
}