循环在Google应用上运行缓慢

huangapple go评论73阅读模式
英文:

For loop on Google apps running slow

问题

function checkFileExists(){
  var range = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getDataRange();
  var values = range.getRichTextValues();
  for (var row = 2; row<values.length; row++) {
    if (!values[row].join("")) break;
    for(var col = 2; col<values[row].length; col++) {
      if (!values[row][col].getLinkUrl()) continue;
      if(!(DriveApp.getFolderById(values[row][col].getLinkUrl().replace(/^.+\//, '')).getFiles()).hasNext()){
        range.getCell(row+1,col+1).setBackground("#e06666");
      }
      else{
        range.getCell(row+1,col+1).setBackground("#93c47d");
      }
    }
  }
}
function checkFileExists2() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  var startRow = 3; // Start row for the range
  var lastRow = sheet.getLastRow();
  var lastColumn = sheet.getLastColumn();
  var range = sheet.getRange(startRow, 3, lastRow - startRow + 1, lastColumn - 2);
  var values = range.getRichTextValues();
  var { rows, requests } = values.reduce((o, row, i) => {
    row.forEach((cell, j) => {
      var link = cell.getLinkUrl();
      if (link) {
        var folderId = link.split("/").pop();
        var q = `'${folderId}' in parents and trashed=false`;
        var fields = "files(id)";
        o.rows.push(sheet.getRange(i + startRow, j + 3).getA1Notation());
        o.requests.push({
          method: "GET",
          endpoint: `https://www.googleapis.com/drive/v3/files?corpora=allDrives&includeItemsFromAllDrives=true&supportsAllDrives=true&pageSize=1&q=${encodeURIComponent(q)}&fields=${encodeURIComponent(fields)}`,
        });
      }
    });
    return o;
  }, { rows: [], requests: [] });
  var results = BatchRequest.EDo({ batchPath: "batch/drive/v3", requests });
  var res = results.reduce((o, { files }, i) => {
    o[files.length > 0 ? "#93c47d" : "#e06666"].push(rows[i]);
    return o;
  }, { "#e06666": [], "#93c47d": [] });
  Object.entries(res).forEach(([k, v]) => {
    if (v.length > 0) {
      sheet.getRangeList(v).setBackground(k);
    }
  });
}
英文:

I have a spreadsheet with Links to drive folders and I'm trying to see which ones are empty or not.
I know the spreadsheet urls start at C3 and was trying to future proof it by just searching until the last row but the runtime is slowing down the more rows i have.

Here is my current script:

function checkFileExists(){
var range = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getDataRange();
var values = range.getRichTextValues();
for (var row = 2; row&lt;values.length; row++) {
if (!values[row].join(&quot;&quot;)) break;
for(var col = 2; col&lt;values[row].length; col++) {
if (!values[row][col].getLinkUrl()) continue;
if(!(DriveApp.getFolderById(values[row][col].getLinkUrl().replace(/^.+\//, &#39;&#39;)).getFiles()).hasNext()){
range.getCell(row+1,col+1).setBackground(&quot;#e06666&quot;);
}
else{
range.getCell(row+1,col+1).setBackground(&quot;#93c47d&quot;);
}
}
}
}

Is there a way to make this more efficient and reduce the runtime?

Edit: Thanks to Tanaike's answer I'm now using this code, it works great.
Starts from C3 and goes to the last piece of data in the sheet (that is in column C or further)

function checkFileExists2() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var startRow = 3; // Start row for the range
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var range = sheet.getRange(startRow, 3, lastRow - startRow + 1, lastColumn - 2);
var values = range.getRichTextValues();
var { rows, requests } = values.reduce((o, row, i) =&gt; {
row.forEach((cell, j) =&gt; {
var link = cell.getLinkUrl();
if (link) {
var folderId = link.split(&quot;/&quot;).pop();
var q = `&#39;${folderId}&#39; in parents and trashed=false`;
var fields = &quot;files(id)&quot;;
o.rows.push(sheet.getRange(i + startRow, j + 3).getA1Notation());
o.requests.push({
method: &quot;GET&quot;,
endpoint: `https://www.googleapis.com/drive/v3/files?corpora=allDrives&amp;includeItemsFromAllDrives=true&amp;supportsAllDrives=true&amp;pageSize=1&amp;q=${encodeURIComponent(q)}&amp;fields=${encodeURIComponent(fields)}`,
});
}
});
return o;
}, { rows: [], requests: [] });
var results = BatchRequest.EDo({ batchPath: &quot;batch/drive/v3&quot;, requests });
var res = results.reduce((o, { files }, i) =&gt; {
o[files.length &gt; 0 ? &quot;#93c47d&quot; : &quot;#e06666&quot;].push(rows[i]);
return o;
}, { &quot;#e06666&quot;: [], &quot;#93c47d&quot;: [] });
Object.entries(res).forEach(([k, v]) =&gt; {
if (v.length &gt; 0) {
sheet.getRangeList(v).setBackground(k);
}
});
}

答案1

得分: 0

我相信您的目标如下。

  • 您希望减少脚本的处理成本。

修改要点:

  • 在您的脚本中,setBackground 在循环中使用。在这种情况下,处理成本会很高。
  • 另外,我认为DriveApp.getFolderById(values[row][col].getLinkUrl().replace(/^.+\//, &#39;&#39;)).getFiles()).hasNext()的成本可能可以降低。

在这种情况下,我考虑以下两种模式。

模式1:

在这种模式中,使用Drive服务(DriveApp)来检查文件夹中文件的存在。

function checkFileExists1() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  var range = sheet.getRange(&quot;C3:C&quot; + sheet.getLastRow());
  var values = range.getRichTextValues();
  var colors = values.map(([c]) =&gt; {
    var link = c.getLinkUrl();
    if (link) {
      var folderId = link.split(&quot;/&quot;).pop();
      var files = DriveApp.getFolderById(folderId).getFiles();
      return [files.hasNext() ? &quot;#93c47d&quot;: &quot;#e06666&quot;];
    }
    return [null]
  });
  range.setBackgrounds(colors);
}

模式2:

在这种模式中,使用Drive API来检查文件夹中文件的存在。在这种情况下,请在高级Google服务中启用Drive API。而且,此示例使用批处理请求。使用批处理请求时,可以降低处理成本。参考作者:我

但是,不幸的是,在当前阶段,批处理请求不能直接与Google Apps Script一起使用。因此,我创建了一个Google Apps Script库。在运行脚本之前,请安装它。参考作者:我

function checkFileExists2() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  var range = sheet.getRange(&quot;C3:C&quot; + sheet.getLastRow());
  var values = range.getRichTextValues();
  var { rows, requests } = values.reduce((o, [c], i) =&gt; {
    var link = c.getLinkUrl();
    if (link) {
      var folderId = link.split(&quot;/&quot;).pop();
      var q = `&#39;${folderId}&#39; in parents and trashed=false`
      var fields = &quot;files(id)&quot;
      o.rows.push(`C${i + 3}`);
      o.requests.push({
        method: &quot;GET&quot;,
        endpoint: `https://www.googleapis.com/drive/v3/files?corpora=allDrives&amp;includeItemsFromAllDrives=true&amp;supportsAllDrives=true&amp;pageSize=1&amp;q=${encodeURIComponent(q)}&amp;fields=${encodeURIComponent(fields)}`,
      });
    }
    return o;
  }, { rows: [], requests: [] });
  var results = BatchRequest.EDo({ batchPath: &quot;batch/drive/v3&quot;, requests });
  var res = results.reduce((o, { files }, i) =&gt; {
    o[files.length &gt; 0 ? &quot;#93c47d&quot;: &quot;#e06666&quot;].push(rows[i]);
    return o;
  }, { &quot;#e06666&quot;: [], &quot;#93c47d&quot;: [] });
  Object.entries(res).forEach(([k, v]) =&gt; {
    if (v.length &gt; 0) {
      sheet.getRangeList(v).setBackground(k);
    }
  });
}

参考资料:

英文:

I believe your goal is as follows.

  • You want to reduce the process cost of your script.

Modification points:

  • In your script, setBackground is used in a loop. In this case, the process cost will be high.
  • Also, I thought that the cost of DriveApp.getFolderById(values[row][col].getLinkUrl().replace(/^.+\//, &#39;&#39;)).getFiles()).hasNext() might be able to be reduced.

In this case, I thought that following 2 patterns.

Pattern 1:

In this pattern, the Drive service (DriveApp) is used for checking the existence of files in the folders.

function checkFileExists1() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  var range = sheet.getRange(&quot;C3:C&quot; + sheet.getLastRow());
  var values = range.getRichTextValues();
  var colors = values.map(([c]) =&gt; {
    var link = c.getLinkUrl();
    if (link) {
      var folderId = link.split(&quot;/&quot;).pop();
      var files = DriveApp.getFolderById(folderId).getFiles();
      return [files.hasNext() ? &quot;#93c47d&quot;: &quot;#e06666&quot;];
    }
    return [null]
  });
  range.setBackgrounds(colors);
}

Pattern 2:

In this pattern, Drive API is used for checking the existence of files in the folders. In this case, please enable Drive API at Advanced Google services. And, this sample uses the batch request. When the batch request is used, the process cost can be reduced. Ref Author: me

But, unfortunately, in the current stage, the batch request cannot be directly used with the Google Apps Script. So, I have created a Google Apps Script library. Please install it before you run the script. Ref Author: me

function checkFileExists2() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  var range = sheet.getRange(&quot;C3:C&quot; + sheet.getLastRow());
  var values = range.getRichTextValues();
  var { rows, requests } = values.reduce((o, [c], i) =&gt; {
    var link = c.getLinkUrl();
    if (link) {
      var folderId = link.split(&quot;/&quot;).pop();
      var q = `&#39;${folderId}&#39; in parents and trashed=false`
      var fields = &quot;files(id)&quot;;
      o.rows.push(`C${i + 3}`);
      o.requests.push({
        method: &quot;GET&quot;,
        endpoint: `https://www.googleapis.com/drive/v3/files?corpora=allDrives&amp;includeItemsFromAllDrives=true&amp;supportsAllDrives=true&amp;pageSize=1&amp;q=${encodeURIComponent(q)}&amp;fields=${encodeURIComponent(fields)}`,
      });
    }
    return o;
  }, { rows: [], requests: [] });
  var results = BatchRequest.EDo({ batchPath: &quot;batch/drive/v3&quot;, requests });
  var res = results.reduce((o, { files }, i) =&gt; {
    o[files.length &gt; 0 ? &quot;#93c47d&quot;: &quot;#e06666&quot;].push(rows[i]);
    return o;
  }, { &quot;#e06666&quot;: [], &quot;#93c47d&quot;: [] });
  Object.entries(res).forEach(([k, v]) =&gt; {
    if (v.length &gt; 0) {
      sheet.getRangeList(v).setBackground(k);
    }
  });
}

References:

huangapple
  • 本文由 发表于 2023年6月22日 13:10:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76528756.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定