fx Google Apps Script for Sheets

getRange / getValues / setValues — and why batch operations matter

⏱ 15 min

What you'll learn

  • Every sheet call is a network trip
  • The slow way (don't do this)
  • getValues → 2D array

Concept

1. Every sheet call is a network trip

Your script runs on Google's servers; the spreadsheet is a separate service. Each getValue() or setValue() is a round trip. 1,000 rows × 2 calls = 2,000 trips, which can take minutes. Apps Script also stops any run after 6 minutes.

2. The slow way (don't do this)

function slowGST() {
  const sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
  for (let r = 2; r <= sh.getLastRow(); r++) {
    const amt = sh.getRange(r, 3).getValue();     // read 1 cell
    sh.getRange(r, 4).setValue(amt * 0.18);       // write 1 cell
  }
}

3. getValues → 2D array

const values = sh.getRange('A2:C5').getValues();
// [ ['Laptop', 12, 55000],
//   ['Mobile',  3, 18000],
//   ['Chair',   0,  4500],
//   ['Desk',    8, 12000] ]
  • It's an array of rows; each row is an array of cells.
  • Indexes start at 0: values[0][2] = first row, third column = 55000.
  • Empty cells come back as '' (empty string). Dates come back as JavaScript Date objects.

4. The fast way: read once, process, write once

function fastGST() {
  const sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
  const lastRow = sh.getLastRow();
  if (lastRow < 2) return;

  const data = sh.getRange(2, 1, lastRow - 1, 3).getValues();   // A2:C(last)
  const out = data.map(row => {
    const qty = Number(row[1]) || 0;
    const rate = Number(row[2]) || 0;
    const amount = qty * rate;
    return [amount, amount * 0.18, amount * 1.18];               // D, E, F
  });

  sh.getRange(2, 4, out.length, 3).setValues(out);                // one write
  sh.getRange('D1:F1').setValues([['Amount', 'GST', 'Total']]);
}

getRange(row, column, numRows, numColumns) is the most useful form: start cell + size.

5. The size rule for setValues

The array must match the range exactly: same number of rows, and every row with the same number of columns. Otherwise: "The number of rows in the data does not match the number of rows in the range."

A single row is still 2D: [['Amount', 'GST', 'Total']] — note the double brackets.

6. Filtering rows in memory

Low-stock items into a "Reorder" sheet:

function buildReorderList() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const data = ss.getSheetByName('Data').getDataRange().getValues();
  const header = data.shift();                         // remove and keep row 1
  const low = data.filter(row => Number(row[1]) < 5);

  const out = ss.getSheetByName('Reorder') || ss.insertSheet('Reorder');
  out.clearContents();
  out.getRange(1, 1, 1, header.length).setValues([header]);
  if (low.length) out.getRange(2, 1, low.length, header.length).setValues(low);
}

getDataRange() = everything from A1 to the last row/column with data.

7. Batch formatting too

Many range methods have plural versions that take arrays: setBackgrounds, setFontColors, setNumberFormats. For a single format on a whole block, one call on the range is enough: range.setNumberFormat('#,##0').

Common mistakes

Calling getValue/setValue inside a loop. Mismatched array size in setValues. Forgetting arrays are 0-indexed while sheet rows/columns start at 1. Treating '' (empty) as 0 without Number().

Exercises

mediumCreate 2,000 rows of test data (Product, Qty, Rate). Time slowGST vs fastGST using const t = Date.now(); ... Logger.log(Date.now() - t);. Then build the Reorder list and add a column with the shortfall (5 − Qty).
Expand Data to 2,000 rows, read A2:C once, map the output and use one setValues() with a matching 2D array. Filter Qty < 5 and append 5−Qty for Reorder. In the starter 30 rows, 15 reorder rows have a combined shortfall of 45. Guard empty results before constructing a zero-height range. Compare results as well as elapsed time.

Quiz

What does getValues() return?
A 2D array — rows of cells
values[0][0] refers to which cell of the range?
The top-left one
Maximum run time of a script?
6 minutes
getRange / getValues / setValues — and why batch operations matter · Automation | ExcelWalaa