Redact Rows For Prompt

Co-Pilot writes much better code when it sees a real-looking sample row. Real-looking is the point: it shouldn't be a real customer. redactRowsForPrompt(rows, opts) takes rows (a 2D array with a header row, or row objects) and replaces emails, phone numbers and any columns you name with realistic fakes that keep the same type and length, so the shape of the data survives and the customer doesn't.

When to use

  • Sample rows for a Co-Pilot brief, alongside your header map
  • Bug reports and support questions that need “a row like this”
  • Demo copies of a client sheet

How to use this snippet

/** * Mask emails, phone numbers and named columns with same-shape, same-length fakes. * Numbers stay numbers, dates stay dates, strings keep their length. * * @param {Array<Array>|Object[]} rows 2D array (first row = headers) or row objects * @param {Object=} opts { columns: ['Name', 'Company'], keepHeader: true } * @return {Array<Array>|Object[]} same shape as the input */ function redactRowsForPrompt(rows, opts) { opts = opts || {}; var named = (opts.columns || []).map(function (c) { return String(c).toLowerCase(); }); var EMAIL = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi; var PHONE = /\+?\d[\d\s().-]{6,}\d/g; var LETTERS = 'abcdefghijklmnopqrstuvwxyz'; var seq = 0; function sameShape(s) { // letters → letters, digits → digits, keep punctuation and case seq++; return String(s).replace(/[A-Za-z0-9]/g, function (ch, i) { var n = (i * 7 + seq * 13) % 26; if (/\d/.test(ch)) return String(n % 10); var c = LETTERS.charAt(n); return ch === ch.toUpperCase() ? c.toUpperCase() : c; }); } function fakeEmail(m) { return sameShape(m); } // same length, still looks like an email function fakeValue(v) { if (v instanceof Date) return new Date(2026, 0, 1 + (++seq % 28)); if (typeof v === 'number') { var digits = String(Math.abs(Math.round(v))).length; var n = Math.pow(10, digits - 1) + (++seq % 9); return v < 0 ? -n : n; } if (typeof v === 'boolean' || v === '' || v == null) return v; return sameShape(v); } function scrub(v) { if (typeof v !== 'string') return v; return v.replace(EMAIL, fakeEmail).replace(PHONE, function (m) { return sameShape(m); }); } function cell(header, v) { return named.indexOf(String(header).toLowerCase()) !== -1 ? fakeValue(v) : scrub(v); } if (rows.length && Array.isArray(rows[0])) { var header = rows[0]; return rows.map(function (r, i) { if (i === 0) return opts.keepHeader === false ? r.map(function () { return ''; }) : r.slice(); return r.map(function (v, c) { return cell(header[c], v); }); }); } return rows.map(function (o) { var out = {}; Object.keys(o).forEach(function (k) { out[k] = cell(k, o[k]); }); return out; }); }

Example

function logSampleRowsForBrief() { var sh = SpreadsheetApp.getActive().getSheetByName('Customers'); var rows = sh.getRange(1, 1, 3, sh.getLastColumn()).getValues(); // header + 2 rows var safe = redactRowsForPrompt(rows, { columns: ['Name', 'Total'] }); console.log(safe.map(function (r) { return r.join(' | '); }).join('\n')); } // Id | Name | Email | Note | Total // 1042 | Nub Pwdkr | aho.cjqxe@szgn.bip | Call +7 (296) 185-3018 | 10005

Tips:

  • The automatic patterns catch emails and phone-like numbers inside any text cell. Names, companies, addresses and IDs need to be listed in columns.
  • Named numeric columns keep their number of digits, so a 5-digit order total stays a 5-digit number.
  • Always read the output before you paste it. Redaction is a safety net, not a guarantee: free-text notes can hide personal details the patterns don't recognize.

Tip: paste the redacted rows under your header map in the NitroGAS Co-Pilot brief, so the draft sees realistic data without seeing a real customer.

Happy Coding!

NitroGAS Chrome Extension

Want this snippet handy inside the Apps Script editor? NitroGAS gives you free themes and snippets — plus optional Co-Pilot when you want a boost (1-week, 1-month, or yearly passes — no subscription). Happy Coding!

Get the Extension