How can I speed up my Office Scripts code so that it doesn't fail in Power Automate?

Rac*_*ury 3 power-automate office-scripts

I have been asked to pull data from an Excel online spreadsheet on Sharepoint into Power BI to create a dashboard - no problem, right? Well, one of the 'data points' is actually fill colour of the cell indicating status. I did some googling and managed to write an Office Script that translates the fill colour to the status and got everything to work but I was still running the Office Script manually (and it was taking 3 hours to run).

So I turned to Power Automate and scheduled it to run the script, but it fails every time and I think it's to do with how long the Office Script takes to run because when I reduce the range that it's running over (significantly) it works. Any help to solve this will be greatly appreciated. Here's my code:

function main(workbook: ExcelScript.Workbook)
{
  
  //disable auto calculation mode

  workbook.getApplication().setCalculationMode(ExcelScript.CalculationMode.manual);

  let selectedSheet = workbook.getWorksheet("ProjectsColourFill");
  let projects = workbook.getWorksheet("Projects");
  // Paste range at 1:1048576 on selectedSheet from 1:1048576 on projects
  selectedSheet.getRange("1:1048576").copyFrom(projects.getRange("1:1048576"), ExcelScript.RangeCopyType.all, false, false);

  let sheet1 = workbook.getWorksheet("Sheet1");
  // Paste range at E3 on selectedSheet from E1:NP1 on sheet1
  selectedSheet.getRange("E3").copyFrom(sheet1.getRange("E1:NP1"), ExcelScript.RangeCopyType.values, false, false);

  selectedSheet.getRange("3:3").setNumberFormatLocal("dd/mm/yyyy;@");

  let cell = selectedSheet.getRange("H7");
  let format = cell.getFormat().getFill().getColor();

  let range = selectedSheet.getRange("H7:NP320");

  // Get the size boundaries of the range.
  let rows = range.getRowCount();
  let cols = range.getColumnCount();


  // Iterate over the range.
  for (let row = 0; row < rows; row++) {
    for (let col = 0; col < cols; col++) {


      if (range.getCell(row, col).getValue() != "") {
         // Get colour of current cell

           // If colour is one of the three status, fill with status
        if (range.getCell(row, col).getFormat().getFill().getColor() == '#DA9694') {
                range.getCell(row,col).setValue("Planned Dates TBC");
            }
        if (range.getCell(row, col).getFormat().getFill().getColor() == '#92D050') {
              range.getCell(row, col).setValue("Booked");
            }
        if (range.getCell(row, col).getFormat().getFill().getColor() == '#E26B0A') {
              range.getCell(row, col).setValue("Prospective");
            }
  
      }
     // console.log(range.getCell(row,col).getAddress());

    }
  }

  //turn it back on
  workbook.getApplication().setCalculationMode(ExcelScript.CalculationMode.automatic);


}
Run Code Online (Sandbox Code Playgroud)

Yut*_*ang 7

您可以考虑以下几点:

  • 尽可能避免在循环内调用 Excel API。
  • 如果可以重用,请尝试将 API 结果捕获到变量中。
  • 不要迭代单元格并调用cell.getValue(),而是尝试将整个范围的所有值检索到二维数组(即let values = range.getValues())中并迭代该数组。
  • 设置单元格值时也是如此 - 使用range.setValues(values)而不是cell.setValue()在循环内重复调用。
  • 不要处理整个工作表中的所有单元格,而是尝试worksheet.getUsedRange()仅处理具有值的单元格之类的事情。
  • 本文讨论了一些良好的性能技巧:https://learn.microsoft.com/en-us/office/dev/scripts/develop/web-client-performance

下面是我尝试优化您的一些代码。我确信仍有改进的空间(例如,切换到getUsedRange()等),但希望您能明白它的想法:

function main(workbook: ExcelScript.Workbook) {

  //disable auto calculation mode
  workbook.getApplication().setCalculationMode(ExcelScript.CalculationMode.manual);

  let selectedSheet = workbook.getWorksheet("ProjectsColourFill");
  let projects = workbook.getWorksheet("Projects");
  // Paste range at 1:1048576 on selectedSheet from 1:1048576 on projects
  selectedSheet.getRange("1:1048576").copyFrom(projects.getRange("1:1048576"), ExcelScript.RangeCopyType.all, false, false);

  let sheet1 = workbook.getWorksheet("Sheet1");
  // Paste range at E3 on selectedSheet from E1:NP1 on sheet1
  selectedSheet.getRange("E3").copyFrom(sheet1.getRange("E1:NP1"), ExcelScript.RangeCopyType.values, false, false);

  selectedSheet.getRange("3:3").setNumberFormatLocal("dd/mm/yyyy;@");

  let cell = selectedSheet.getRange("H7");
  let format = cell.getFormat().getFill().getColor();

  let range = selectedSheet.getRange("H7:NP320");
  let values = range.getValues();

  // Get the size boundaries of the range.
  let rows = range.getRowCount();
  let cols = range.getColumnCount();

  // Iterate over the range.
  for (let row = 0; row < rows; row++) {
    for (let col = 0; col < cols; col++) {

      if (values[row][col] != "") {
        // Get colour of current cell
        let color = range.getCell(row, col).getFormat().getFill().getColor();

        // If colour is one of the three status, fill with status
        if (color == '#DA9694') {
          values[row][col] = "Planned Dates TBC";
        }
        if (color == '#92D050') {
          values[row][col] = "Booked";
        }
        if (color == '#E26B0A') {
          values[row][col] = "Prospective";
        }
      }
    }
  }

  range.setValues(values);

  //turn it back on
  workbook.getApplication().setCalculationMode(ExcelScript.CalculationMode.automatic);
}
Run Code Online (Sandbox Code Playgroud)