Find rows with particular value from a Google Spre

2019-03-07 01:52发布

I am new to spreadsheet scripting. I am generating a report (new sheet) based on another sheet where I enter values daily. In Apps Script I first generate the sheet then loop through the data range retrieved from that input sheet. After that I have to merge values based on dates and categories.
Now my report format is such that rows are categories and dates are columns. So if in input if there is another value with same date and same category I have to add the value.

My problem is how to check if the value with same date and category exists in the report and I DO NOT want to use loops as I am already in loops so that will make the process run very very slow.

2条回答
Fickle 薄情
2楼-- · 2019-03-07 02:48

I don't think it is possible to do it without some looping. Since this operation is carried out server side without the need to make calls to the spreadsheet it would take a very small amount of time even with a very large dataset.

If your script is already slow it more than likely because of inefficiencies/ delays in some other part of the script. I have a script which duplicates a spreadsheet and renames it, just those to operations take between 5 & 8 seconds.

As an example:

  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 
}

This operation carried out on a dataset 56 columns x 1000 rows completes in 0.88 seconds with the search value in the last cell of the range.

查看更多
叼着烟拽天下
3楼-- · 2019-03-07 02:49

Your report sounds a fair bit like a Pivot Table with categories in rows, dates in columns, and SUM(Value) as the data field. To reproduce this with a script report, you can use an Object variable that maps between a key and a "value" Object:

This probably isn't your exact use case (it assumes you need to generate a new report from a possibly-large stack of feeder data, but it should demonstrate how you can use nested Objects to simplify / internalize the lookup process, including testing for undefined values and enforcing a rectangular output.

// 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);
查看更多
登录 后发表回答