在不使用循环的情况下从Google电子表格中查找具有特定价值的行?

时间:2018-02-21 18:33:02

标签: google-apps-script google-sheets

我是电子表格脚本的新手。我正在根据另一张表格生成一份报告(新表格),我每天都会输入值。在Apps脚本中,我首先生成工作表然后遍历从该输入表检索到的数据范围。 之后,我必须根据日期和类别合并值 现在我的报告格式是行是类别而日期是列。 因此,如果输入中有另一个具有相同日期和相同类别的值,则必须添加该值。

我的问题是如何检查报告中是否存在具有相同日期和类别的值,并且我不想使用循环,因为我已经在循环中,这样会使进程运行得非常慢。

2 个答案:

答案 0 :(得分:0)

我认为没有一些循环就可以做到这一点。由于此操作在服务器端执行而无需调用电子表格,因此即使使用非常大的数据集也只需要很短的时间。

如果您的脚本已经很慢,那很可能是因为脚本的其他部分效率低下/延迟。我有一个脚本,它复制了一个电子表格并重命名它,只有那些操作需要5到5之间。 8秒

举个例子:

  function test(){
  var ss = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
  var value = "agdsdfgsdfg" // This value is in cell BD1000
  for(var i = 0; i < ss.length; i ++){
   if(ss[i].indexOf(value)>=0){
     var x = ss[i].indexOf(value) + 1;
     break;
   }
  }
  var y = i + 1
 // var x & y are the row, cell coordinates of value in the data range 
}

对数据集56列x 1000行执行的此操作在0.88秒内完成,搜索值位于范围的最后一个单元格中。

答案 1 :(得分:0)

您的报告听起来有点像数据透视表,其中行中的类别,列中的日期以及作为数据字段的SUM(值)。要使用脚本报告重现此问题,您可以使用Object变量来映射密钥和&#34;值&#34; Object

这可能不是您的确切用例(它假设您需要从可能大量的馈送器数据生成新报告,但它应该演示如何使用嵌套Object来简化/内化查找过程,包括测试未定义的值和强制执行矩形输出。

// Make an array of the data, to limit use of the slow spreadsheet interface.
var inputs = SpreadsheetApp.openById(<id>).getSheetByName(<dataSheetName>)
    .getDataRange().getValues();
// The first row is probably column headers from the input sheet, and
// doesn't likely contain useful data that you want in your report.
var headers = inputs.splice(0, 1);
var report = {};
for(var row = 0; row < inputs.length; ++row) {
    // Change these indexes (0, 1, 2) to the proper values.
    // Also do any necessary formatting / validation, etc. for "category" and "date".
    var category = inputs[row][0];
    var date = inputs[row][1];
    var value = inputs[row][2];
    // If this category doesn't exist, default construct its report object.
    // For each category, a nested object is used to store the date-value pair.
    if(!report[category]) {
        report[category] = {};
    }
    // Otherwise, if the date is not yet seen for the category, set
    // the value. If it is seen, increment the stored value by the new value.
    if(!report[category][date]) {
        report[category][date] = value;
    } else {
        // Treat this as numeric addition, not string concatenation.
        report[category][date] += value - 0;
    }
}
// To print your report, you need a header you can index against.
var outputHeader = [];
for(var category in report) {
    for(var date in category) {
        outputHeader.push(date);
    }
}
// Sort this header row. If the dates are strings that don't simply
// coerce to proper Date objects, you'll need to write your own sort() method.
// (You don't technically need to sort, but if you don't then the dates
// won't be "in order" when the report prints.)
outputHeader.sort();
// After sorting, add a row label for the header of sorted dates.
outputHeader.splice(0, 0, "Category / Date");

// Serialize the report object into an array[][];
var output = [outputHeader];
var totalColumns = outputHeader.length;
for(var category in report) {
    // Initialize each row with the row label in the 0 index position.
    var row = [category];
    for(var date in category) {
        var index = outputHeader.indexOf(date);
        row[index] = category[date];
    }
    // Unless you are guaranteed that every category has a value for every date
    // in the report, you need to ensure that the row has a value at each index.
    // (This is a good idea anyway, to ensure that you have a rectangular array.)
    var filled = Object.keys(row);
    // We can start at 1 since we know that every row starts with its category.
    for(var col = 1; col < totalColumns; ++col) {
        if(filled.indexOf(String(col)) < 0) {
            row[col] = "";
        }
    }
    output.push(row);
}
SpreadsheetApp.openById(<id>).getSheetByName(<reportSheetName>)
    .getRange(1, 1, output.length, output[0].length).setValues(output);