Google表格单元格的重新计算

时间:2018-12-04 15:20:48

标签: google-apps-script google-sheets google-sheets-formula custom-function

我正在创建一个棋盘游戏,因此我决定选择Google床单。我已将问题简化为一个由一张纸和一个脚本组成的最小示例。


情况

以下几点是我的骰子表:

  • 单元格 B2:C5 包含可用的骰子。第一列包含骰子小精灵,第二个逗号分隔的骰子面编号。
    • 游戏设计视角:艺术家可以更改骰子图像。设计者可以更改骰子的面数。两种类型的更改都会自动传播到引用骰子的单元格。
  • 单元格 E2:I2 E10:I10 包含特定的 throw 。在每个投掷中, B 列中有 1至5 引用骰子。
    • 游戏设计视角:存在许多不同的游戏项目,每个项目可能具有不同的骰子来确定动作的结果。设计人员可以添加或删除对骰子的引用,它将触发特定单元格的自动重新计算(在我们的示例中,这些单元格为 K2 K10 )。
  • K2 K10 开头的单元格将DICEFACES函数的结果应用于范围 E2:I2 和< strong> E10:I10 。
    • 游戏设计视角:骰子面孔矩阵将进一步用于计算骰子概率。为了简化示例,我将矩阵本身视为最终结果。
  • DICEFACES是我在脚本编辑器中创建的自定义函数 与样式表关联的文件Code.gs。它返回一个矩阵 所提供范围内的骰子所对应的骰子面。它的 正文如下:

    function DICEFACES(unused_ref_to_range_containing_dices)
    {
      var app  = SpreadsheetApp;
      var spr  = app.getActiveSheet();
    
      // In the end this array will hold the dice faces. For example two
      // 1d6 dices would result in [[1,2,3,4,5,6],[1,2,3,4,5,6]].
      //
      var Dices = [];
    
      // The the formula inside the active cell (i.e. the cell on which
      // we are calling this function). This is a string like:
      //
      // "=DICEFACES(E2:I2)"
      //
      var active_formula = spr.getActiveRange().getFormula();
    
      // Set item_range to the one pointed to by the formula. This could
      // be a range like E2:I2.
      //
      var item_range = spr.getRange(active_formula.match(/=\w+\((.*)\)/i)[1]);
    
      // Loop over dice cells in the item_range.
      //
      for (var i = 1; i <= item_range.getNumColumns(); i++)
      {
        // "=B2", "=B3", ...
        //
        var dice_formula = item_range.getCell(1, i).getFormula();
    
        // As soon as we encounter an empty formula, we skip (i.e. there are
        // no more dices).
        //
        if (dice_formula == "")
        {
          break;
        }
    
        // A reference to the cell containing the dice image. We don't really
        // need the image, the dice faces are of greater importance to us.
        //
        var dice_cell = spr.getRange(dice_formula.substr(1));
    
        // Move one column to the right prior to the dice_cell and retreive
        // the value of the cell. This is a string like "1,2,3,4,5,6".
        //
        var dice_csv = dice_cell.offset(0, 1).getValue();
    
        // Convert the CSV string to a javascript array like [1,2,3,4,5,6]
        // and push it to Dices.
        //
        Dices.push(dice_csv.split(",").map(Number));
      }
      return Dices;
    }
    

问题

问题是,当我更改 C 列中的骰子面时,DICEFACE公式没有重新计算。在创建screenshot之前,我在单元格 C2 中添加了,4后缀,并且您可以看到在单元格 N2中没有4。强>。但是,如果我或者重新保存Code.gs脚本文件更改 E2:I2 中的骰子,则会立即发生重新计算

我很确定我知道问题出在哪里:因为我正在遍历脚本中的单元格,所以工作表应用本身看不到 C 列和 K2 K10 中的公式。查看我的工作表,单元格引用可能类似于:

K4  <-- E2:I2   <-- B2, B3 (C is not here)
K10 <-- E10:I10 <-- B4, B5 (C is not here)

我的符号A <-- B的意思是If there's a change in range B, update cell A


问题

修改骰子面后,应如何更改以立即进行自动重新计算?如果不可能,那么完成我任务的最佳方法是什么?

1 个答案:

答案 0 :(得分:3)

  

问题是,当我更改 C 列中的骰子面时,DICEFACE公式没有重新计算。

DIFACE是自定义函数,当打开电子表格并且自定义函数参数值更改时,将重新计算自定义函数。

考虑到上述情况,为了最大程度地减少对自定义函数的更改,请添加第二个参数作为触发器。

更改正则表达式

/=\w+\((.*)\)/i

/=\w+\((.*),.*\)/i

然后通过以下方式调用您的自定义函数

=DICEFACES(E2:I2,C2)

=DICEFACES(E2:I2,C2:C5)


我使用逗号,假设使用的是Google Sheets参数分隔符,但某些电子表格可以使用分号。


OP自定义功能的修改版本

/**
 * Returns a matrix of dice faces corresponding to the dices in the provided range.
 *
 * @param {Array} unused_ref_to_range_containing_dices Reference to range. i.e. E2:I2
 * @param {String|Number|Date|Array} ref_as_trigger Reference to a range used as trigger. i.e. C2 or C2:C5
 * @return array
 * @customfunction
 */
function DICEFACES(unused_ref_to_range_containing_dices,ref_as_trigger)
{
  var app  = SpreadsheetApp;
  var spr  = app.getActiveSheet();

  // In the end this array will hold the dice faces. For example two
  // 1d6 dices would result in [[1,2,3,4,5,6],[1,2,3,4,5,6]].
  //
  var Dices = [];

  // The the formula inside the active cell (i.e. the cell on which
  // we are calling this function). This is a string like:
  //
  // "=DICEFACES(E2:I2)"
  //
  var active_formula = spr.getActiveRange().getFormula();

  // Set item_range to the one pointed to by the formula. This could
  // be a range like E2:I2.
  //
  var item_range = spr.getRange(active_formula.match(/=\w+\((.*),.*\)/i)[1]); // CHANGED

  // Loop over dice cells in the item_range.
  //
  for (var i = 1; i <= item_range.getNumColumns(); i++)
  {
    // "=B2", "=B3", ...
    //
    var dice_formula = item_range.getCell(1, i).getFormula();

    // As soon as we encounter an empty formula, we skip (i.e. there are
    // no more dices).
    //
    if (dice_formula == "")
    {
      break;
    }

    // A reference to the cell containing the dice image. We don't really
    // need the image, the dice faces are of greater importance to us.
    //
    var dice_cell = spr.getRange(dice_formula.substr(1));

    // Move one column to the right prior to the dice_cell and retreive
    // the value of the cell. This is a string like "1,2,3,4,5,6".
    //
    var dice_csv = dice_cell.offset(0, 1).getValue();

    // Convert the CSV string to a javascript array like [1,2,3,4,5,6]
    // and push it to Dices.
    //
    Dices.push(dice_csv.split(",").map(Number));
  }
  return Dices;
}