Scriptdrill

JavaScript for Sheets Updated November 2026

Objects: keys, values and entries

An object maps keys to values ({name: "Ana", qty: 5}); Object.keys(obj) lists the keys, Object.values(obj) the values and Object.entries(obj) [key, value] pairs. The most useful sheet pattern is turning each row into an object by header: rows.map(r => Object.fromEntries(headers.map((h, i) => [h, r[i]]))), so code reads row.qty instead of row[3].

Syntax

Object.keys(obj) | Object.values(obj) | Object.entries(obj) | Object.fromEntries(pairs)

Examples

const sheet = SpreadsheetApp.getActiveSheet();
const [headers, ...rows] = sheet.getDataRange().getValues();
const records = rows.map(r => Object.fromEntries(headers.map((h, i) => [h, r[i]])));
Logger.log(JSON.stringify(records[0]));

Result: {"Item":"Pens","Qty":12,"Price":2.5}

const counts = {};
records.forEach(rec => counts[rec.Status] = (counts[rec.Status] || 0) + 1);
Logger.log(JSON.stringify(counts));

Result: {"Open":7,"Closed":12}

const out = Object.entries(counts).map(([status, n]) => [status, n]);
sheet.getRange("H1:I" + out.length).setValues(out);

Result: H1:I2 holds Open 7 and Closed 12

Common mistakes

  • rec.qty is undefined: keys are case-sensitive and come from the header text exactly, including spaces; normalize headers with trim and toLowerCase if needed.
  • Writing records with setValues fails: setValues needs arrays, so convert back with records.map(rec => headers.map(h => rec[h])).
  • Object.keys order is not what you expected: integer-like keys come first in ascending order, then string keys in insertion order.
  • JSON.stringify of a record shows dates as strings: that is normal for logging; the object still holds Date objects.

Practice Objects: keys, values and entries

These drills check your answer on live data, free, no account needed.

  • Group by a fieldReturn an object that maps each value of `item[key]` to the array of items with that value, in their original order. Values become object keys, so the number 2 becomes the key `"2"`. Keys are case-sensitive: `"Paid"` and `"paid"` are different groups.
  • Count by a fieldReturn an object that maps each value of `item[key]` to how many items have it. Skip items where the field is missing or `undefined`.
  • Pivot rows into a lookupTurn flat rows into a two-level lookup: `result[row[rowKey]][row[colKey]]` is the sum of `row[valueKey]` over every row with that pair. Pairs that never appear are absent. A sum of 0 is still kept.

Questions

Object or Map for lookups?

Object works fine for string keys; Map keeps any key type and insertion order and is slightly cleaner for large lookups: new Map(rows.map(r => [r[0], r])).

How do I copy an object without changing the original?

Use the spread operator for a shallow copy: const copy = {...rec, Status: "Closed"} leaves rec unchanged.

Related

  • map, filter and reduceUse map, filter and reduce on Google Sheets data in Apps Script: transform rows, keep rows that match a condition, sum a column, chain them, write back.
  • JSON.parse and JSON.stringifyUse JSON.parse and JSON.stringify in Google Apps Script: turn an API response into objects, write nested fields to a sheet, store objects as properties.
  • getRange and getValuesRead Google Sheets data with Apps Script: getRange by A1 notation or row and column numbers, then getValue for one cell or getValues for a 2D array of rows.
  • Deploy as a web appDeploy a Google Apps Script as a web app: write doGet and doPost, return HTML or JSON, read query and POST parameters, set who runs it and who can access it.

Previous: forEach vs for loopsNext: String methods