You have 200 rows and need one email (or one Drive folder, or one export) per client. Nested loops and “remember the last name” logic get messy. groupObjectsByKey(rows, keyOrFn) turns an array of row objects into a plain map of key → [rows…] so the rest of the job is a simple for…in (or Object.keys).
When to use
- Per-client / per-region digests and emails
- Filing exports into one Drive folder per account
- Splitting a big import into batches keyed by type or date
How to use this snippet
/**
* Group an array of row objects by a field name or a key function.
* Returns a plain object: { [key]: Object[] }. Blank/null keys become ''.
*
* @param {Object[]} rows
* @param {string|function(Object):*} keyOrFn e.g. 'Client' or function(r){ return r.Region + '|' + r.Client; }
* @return {Object.<string, Object[]>}
*/
function groupObjectsByKey(rows, keyOrFn) {
var out = {};
if (!rows || !rows.length) return out;
var getKey = typeof keyOrFn === 'function'
? keyOrFn
: function (row) { return row[keyOrFn]; };
rows.forEach(function (row) {
var key = getKey(row);
// Keep blank keys in one bucket instead of inventing "undefined"
if (key == null) key = '';
key = String(key);
if (!out[key]) out[key] = [];
out[key].push(row);
});
return out;
}
Example
function emailDigestPerClient() {
var rows = readSheetData_(); // [{ Client, Email, Amount }, ...]
var byClient = groupObjectsByKey(rows, 'Client');
Object.keys(byClient).forEach(function (client) {
var group = byClient[client];
var to = group[0].Email;
var body = group.map(function (r) {
return r.Amount + ' — ' + (r.Note || '');
}).join('\n');
GmailApp.sendEmail(to, 'Digest for ' + client, body);
});
}
// Compound key: region + client
var byRegionClient = groupObjectsByKey(rows, function (r) {
return String(r.Region || '') + '|' + String(r.Client || '');
});
Tips:
- Keys are always strings (
String(key)). A number42and the string"42"land in the same bucket — usually what you want from a sheet. - Blank / missing keys go under
''. Filter them out before grouping if “no client” rows should be skipped, not emailed. - Order inside each group matches the input order. Sort the input first if you care.
- This is a building block — pair it with filing, emails, or PDF export rather than reinventing the map each time.
Tip: NitroGAS Co-Pilot can wire groupObjectsByKey into an existing “email everyone” loop if you paste the function and say you want one email per Client.
Happy Coding!
