tanaike - Google Apps Script, Gemini API, and Developer Tips

The Thinker

Updated: GAS Library - FilesApp

FilesApp was updated to v1.1.0.

  1. Shared drive got to be able to be used. From the version 1.1.0, the following modification was added.
    1. V8 is used.
    2. As the default setting, the file list is retrieved from both your Google Drive and the shared drive.
      • By this, for example, when the folder ID in the shared Drive is used to ### of const res = FilesApp.createTree("###"), the folder tree of the shared Drive is retrieved, if you have the shared Drive.

And also, FilesApp was updated to v1.1.1.

Hiding and Deleting Rows and Columns on Google Spreadsheet using Google Apps Script

Gists

These are the sample scripts for hiding and deleting rows and columns on Google Spreadsheet using Google Apps Script. I sometimes see the questions for hiding and deleting rows and columns on Spreadsheet at Stackoverflow. So here, I would like to introduce the sample scripts for this.

In this case, when the process costs of the scripts created by using Spreadsheet service and Sheets API are compared, the cost of script created by Sheets API is much lower than that of script created by Spreadsheet service. So when the rows and columns of your Spreadsheet is large and you can use Sheets API, I recommend to use Sheets API.

When '//' in template literal is used in a HTML file in script editor, it is used as a comment start

Gists

Overview

When // in template literal is used in a HTML file in script editor, it is used as a comment start.

const sample = `//`;

For example, when above script is used in a HTML file at the script editor, ;" of const sample =`//`; is used as the comment.

Description

I would like to explain about this bug using the following sample flow.

Flow

  1. Create new Spreadsheet and open the script editor. In this explanation, please use the container-bound script.

Workaround for Retrieving Direct Links of All Sheets from URL of 2PACX- of Web Published Google Spreadsheet

Gists

This is a sample script for retrieving the direct links of all sheets from the URL like https://docs.google.com/spreadsheets/d/e/2PACX-###/pubhtml of the web published Google Spreadsheet. This sample script can be used for the following situation.

  1. The Spreadsheet is published to Web and the URL like https://docs.google.com/spreadsheets/d/e/2PACX-###/pubhtml is known
  2. You are not the owner of Google Spreadsheet.
  3. You don’t know the Spreadsheet ID and Sheet IDs.

Under above situation, unfortunately, the direct links of each sheet cannot be directly retrieved. I think that this is the specification of Google side. So in this post, I would like to introduce a workaround for retrieving the direct links of each sheet under above situation.

Updating a File with Resumable Upload using Drive API

Gists

This is a sample flow for updating a file with the resumable upload using Drive API.

Sample situation:

In this answer, as a sample situation, it supposes that a text file in Google Drive is updated by the resumable upload with the multiple chunks. And as the method for requesting, I use the curl command.

I prepared 2 files for 2 chunks. As the test situation, the 2 chunks of 262,144 bytes and 37,856 bytes are uploaded. So total upload size is 300,000 bytes. Those filenames are data1.txt and data2.txt, respectively.

Limitation of Array.prototype.push.apply under V8 for Google Apps Script

Gists

Description

When V8 is enabled, Array.apply has the limitation for the number of elements. When it is over the limitation, an error like RangeError: Maximum call stack size exceeded occurs, while the issue didn’t occur when V8 is disabled. In this case, this issue occurs at both Google Apps Script and Javascript. So please be careful this.

Sample situation

For example, when Array.prototype.push.apply is used for combining the arrays because the process cost of Array.prototype.push.apply is lowest of 3 patterns of Array.prototype.push.apply, Spread syntax and concat, please be careful above issue. Ref

Updated: Expanding A1Notations using Google Apps Script

Gists

This sample script is for expanding a1Notations using Google Apps Script. This was updated from this sample script.

Sample script

function expandA1Notations_(a1Notations, maxRow, maxColumn) {
  maxRow = maxRow || "1000";
  maxColumn = maxColumn || "Z";

  // Ref: https://stackoverflow.com/a/21231012/7108653
  const columnToLetter = column => {
    let temp,
      letter = "";
    while (column > 0) {
      temp = (column - 1) % 26;
      letter = String.fromCharCode(temp + 65) + letter;
      column = (column - temp - 1) / 26;
    }
    return letter;
  };
  const letterToColumn = letter => {
    let column = 0,
      length = letter.length;
    for (let i = 0; i < length; i++) {
      column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1);
    }
    return column;
  };

  const reg1 = new RegExp("^([A-Z]+)([0-9]+)$");
  const reg2 = new RegExp("^([A-Z]+)$");
  const reg3 = new RegExp("^([0-9]+)$");
  return a1Notations.map(e => {
    const a1 = e.split("!");
    const r = a1.length > 1 ? a1[1] : a1[0];
    const [r1, r2] = r.split(":");
    if (!r2) return [r1];
    let rr;
    if (reg1.test(r1) && reg1.test(r2)) {
      rr = [r1.toUpperCase().match(reg1), r2.toUpperCase().match(reg1)];
    } else if (reg2.test(r1) && reg2.test(r2)) {
      rr = [
        [null, r1, 1],
        [null, r2, maxRow]
      ];
    } else if (reg1.test(r1) && reg2.test(r2)) {
      rr = [r1.toUpperCase().match(reg1), [null, r2, maxRow]];
    } else if (reg2.test(r1) && reg1.test(r2)) {
      rr = [[null, r1, maxRow], r2.toUpperCase().match(reg1)];
    } else if (reg3.test(r1) && reg3.test(r2)) {
      rr =
        Number(r1) > Number(r2)
          ? [
              [null, "A", r2],
              [null, maxColumn, r1]
            ]
          : [
              [null, "A", r1],
              [null, maxColumn, r2]
            ];
    } else if (reg1.test(r1) && reg3.test(r2)) {
      rr = [r1.toUpperCase().match(reg1), [null, maxColumn, r2]];
    } else if (reg3.test(r1) && reg1.test(r2)) {
      let temp = r2.toUpperCase().match(reg1);
      rr =
        Number(temp[2]) > Number(r1)
          ? [
              [null, temp[1], r1],
              [null, maxColumn, temp[2]]
            ]
          : [temp, [null, maxColumn, r1]];
    } else {
      throw new Error(`Wrong a1Notation: ${r}`);
    }
    const obj = {
      startRowIndex: Number(rr[0][2]),
      endRowIndex: rr.length == 1 ? Number(rr[0][2]) + 1 : Number(rr[1][2]) + 1,
      startColumnIndex: letterToColumn(rr[0][1]),
      endColumnIndex:
        rr.length == 1
          ? letterToColumn(rr[0][1]) + 1
          : letterToColumn(rr[1][1]) + 1
    };
    let temp = [];
    for (let i = obj.startRowIndex; i < obj.endRowIndex; i++) {
      for (let j = obj.startColumnIndex; j < obj.endColumnIndex; j++) {
        temp.push(columnToLetter(j) + i);
      }
    }
    return temp;
  });
}

// When you use this script, please run main().
function main() {
  const a1Notations = ["A1:E3", "B10:W13", "EZ5:FA8", "AAA1:AAB3"];
  const res = expandA1Notations_(a1Notations);
  console.log(res);
}

Result

[
    ["A1","B1","C1","D1","E1","A2","B2","C2","D2","E2","A3","B3","C3","D3","E3"],
    ["B10","C10","D10","E10","F10","G10","H10","I10","J10","K10","L10","M10","N10","O10","P10","Q10","R10","S10","T10","U10","V10","W10","B11","C11","D11","E11","F11","G11","H11","I11","J11","K11","L11","M11","N11","O11","P11","Q11","R11","S11","T11","U11","V11","W11","B12","C12","D12","E12","F12","G12","H12","I12","J12","K12","L12","M12","N12","O12","P12","Q12","R12","S12","T12","U12","V12","W12","B13","C13","D13","E13","F13","G13","H13","I13","J13","K13","L13","M13","N13","O13","P13","Q13","R13","S13","T13","U13","V13","W13"],
    ["EZ5","FA5","EZ6","FA6","EZ7","FA7","EZ8","FA8"],
    ["AAA1","AAB1","AAA2","AAB2","AAA3","AAB3"]
]

Retrieve duplicated cells

When this script is used, the duplicated cells can be retrieved as follows.