Pivot & Export to Multiple Sheets¶
Generate multiple pivot views from a single data source and export to different sheets.
User Story¶
You have sales transaction data with columns: date, product, region, amount. You need to:
- Validate all transaction data
- Create a pivot table: products by region (sum of amounts)
- Create another pivot table: products by month (sum of amounts)
- Export both pivot views to separate sheets
- Keep original data as reference
With gaslamp¶
JavaScript
function cookbook_pivotExport() {
// Step 1: Define schema for transaction data
const schema = {
date: gaslamp.FlameGuards.isString,
product: gaslamp.FlameGuards.isString,
region: gaslamp.FlameGuards.isString,
amount: gaslamp.FlameGuards.isNumber,
};
const sourceSheet = SpreadsheetApp.getActiveSheet();
const workbook = SpreadsheetApp.getActiveSpreadsheet();
// Step 2: Load and validate transaction data
const df = gaslamp.BareFrame.fromSheet(sourceSheet);
const { passed, failed } = gaslamp.FlameFrame.from(df, schema);
if (failed.length > 0) {
console.error(failed.length + ' validation error(s) found:');
console.error(failed.display());
return;
}
console.log(`Loaded ${passed.length} valid transactions`);
// Step 3: Create Pivot 1 — Products by Region
const pivotByRegion = passed
.groupBy(['product', 'region'])
.sum(['amount']);
console.log('Pivot 1 (Products × Region):');
console.log(pivotByRegion.display());
const regionSheet = workbook.getSheetByName('Pivot - By Region')
?? workbook.insertSheet('Pivot - By Region');
pivotByRegion.toSheet(regionSheet);
// Step 4: Add month column for time-based pivot
const withMonth = passed.withColumn('month', (row) => {
const date = new Date(row.get('date'));
return Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy-MM');
});
// Step 5: Create Pivot 2 — Products by Month
const pivotByMonth = withMonth
.groupBy(['product', 'month'])
.sum(['amount']);
console.log('Pivot 2 (Products × Month):');
console.log(pivotByMonth.display());
const monthSheet = workbook.getSheetByName('Pivot - By Month')
?? workbook.insertSheet('Pivot - By Month');
pivotByMonth.toSheet(monthSheet);
// Step 6: Create summary statistics
const summary = passed
.groupBy([])
.sum(['amount']);
const totalAmount = summary.toRows()[0].get('amount');
console.log(`Total sales: ${totalAmount}`);
// Step 7: Export pivot summaries to a dashboard sheet
const uniqueProductCount = passed.groupBy(['product']).length;
const summaryData = gaslamp.BareFrame.fromArrays([
['Metric', 'Value'],
['Total Transactions', passed.length],
['Total Sales Amount', totalAmount],
['Unique Products', uniqueProductCount],
]);
const dashboardSheet = workbook.getSheetByName('Dashboard')
?? workbook.insertSheet('Dashboard');
summaryData.toSheet(dashboardSheet);
console.log('All pivots and dashboard exported');
}
Key gaslamp features used:
- Schema validation with
FlameFrame.from()— validate all data before pivoting .groupBy()with multiple dimensions — products × region, products × month.sum()on grouped data — aggregate amounts per group- Reusable DataFrame — generate multiple pivots from same source
.withColumn()for computed columns — add month from date for time-based pivot- Multiple output sheets — export different pivot views independently
.toSheet()for each view — write each pivot to its own sheet- Dashboard creation — combine summary statistics with
.fromArrays()
Without gaslamp¶
JavaScript
function cookbook_pivotExport_vanilla() {
const sourceSheet = SpreadsheetApp.getActiveSheet();
const workbook = SpreadsheetApp.getActiveSpreadsheet();
// Step 1: Load and validate transaction data
const range = sourceSheet.getDataRange();
const values = range.getValues();
const headers = values[0];
const rows = values.slice(1);
const dateIdx = headers.indexOf('date');
const productIdx = headers.indexOf('product');
const regionIdx = headers.indexOf('region');
const amountIdx = headers.indexOf('amount');
const validated = [];
for (const row of rows) {
if (typeof row[dateIdx] !== 'string' || typeof row[productIdx] !== 'string' ||
typeof row[regionIdx] !== 'string' || typeof row[amountIdx] !== 'number') {
console.warn('Type error in row:', row);
continue;
}
validated.push(row);
}
if (validated.length === 0) {
console.warn('Cannot pivot: no valid rows');
return;
}
console.log(`Loaded ${validated.length} valid transactions`);
// Step 2: Manually create pivot map for products by region
const pivotMapRegion = {};
const productOrder = [];
const regionOrder = [];
for (const row of validated) {
const product = row[productIdx];
const region = row[regionIdx];
const amount = row[amountIdx];
const key = `${product}|${region}`;
if (!pivotMapRegion[key]) {
pivotMapRegion[key] = { product, region, amount: 0 };
if (!productOrder.includes(product)) productOrder.push(product);
if (!regionOrder.includes(region)) regionOrder.push(region);
}
pivotMapRegion[key].amount += amount;
}
// Step 3: Convert pivot map to 2D array (products × regions)
const pivotByRegion = [];
for (const product of productOrder) {
const row = [product];
for (const region of regionOrder) {
const key = `${product}|${region}`;
row.push(pivotMapRegion[key]?.amount || 0);
}
pivotByRegion.push(row);
}
// Step 4: Write pivot by region to sheet
const regionSheet = workbook.getSheetByName('Pivot - By Region')
?? workbook.insertSheet('Pivot - By Region');
const regionHeaders = ['Product', ...regionOrder];
regionSheet.getRange(1, 1, 1, regionHeaders.length).setValues([regionHeaders]);
if (pivotByRegion.length > 0) {
regionSheet.getRange(2, 1, pivotByRegion.length, pivotByRegion[0].length)
.setValues(pivotByRegion);
}
// Step 5: Manually create pivot map for products by month
const pivotMapMonth = {};
const monthOrder = [];
for (const row of validated) {
const product = row[productIdx];
const date = new Date(row[dateIdx]);
const month = Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy-MM');
const amount = row[amountIdx];
const key = `${product}|${month}`;
if (!pivotMapMonth[key]) {
pivotMapMonth[key] = { product, month, amount: 0 };
if (!monthOrder.includes(month)) monthOrder.push(month);
}
pivotMapMonth[key].amount += amount;
}
// Step 6: Convert pivot map to 2D array (products × months)
const pivotByMonth = [];
for (const product of productOrder) {
const row = [product];
for (const month of monthOrder) {
const key = `${product}|${month}`;
row.push(pivotMapMonth[key]?.amount || 0);
}
pivotByMonth.push(row);
}
// Step 7: Write pivot by month to sheet
const monthSheet = workbook.getSheetByName('Pivot - By Month')
?? workbook.insertSheet('Pivot - By Month');
const monthHeaders = ['Product', ...monthOrder];
monthSheet.getRange(1, 1, 1, monthHeaders.length).setValues([monthHeaders]);
if (pivotByMonth.length > 0) {
monthSheet.getRange(2, 1, pivotByMonth.length, pivotByMonth[0].length)
.setValues(pivotByMonth);
}
// Step 8: Calculate summary statistics
let totalAmount = 0;
for (const row of validated) {
totalAmount += row[amountIdx];
}
// Step 9: Write dashboard
const dashboardSheet = workbook.getSheetByName('Dashboard')
?? workbook.insertSheet('Dashboard');
const dashboardHeaders = ['Metric', 'Value'];
const dashboardData = [
['Total Transactions', validated.length],
['Total Sales Amount', totalAmount],
];
dashboardSheet.getRange(1, 1, 1, 2).setValues([dashboardHeaders]);
dashboardSheet.getRange(2, 1, dashboardData.length, 2).setValues(dashboardData);
console.log('All pivots and dashboard exported');
}
Without gaslamp, you need to:
- Manually validate types for each row
- Manually create separate pivot maps for each dimension combination
- Manually track row and column order to preserve insertion order
- Manually iterate through validated data multiple times (once per pivot)
- Manually reconstruct 2D arrays from pivot maps with nested loops
- Manually construct headers by spreading computed dimensions
- Manually handle missing pivot cells with optional chaining and defaults
- Duplicate dimension collection logic (product order, region order, month order)
- Manually calculate summary statistics with reduce-like loops
- Write headers and data separately for each output sheet
- No schema validation on results
Key Techniques¶
- Validate before pivoting: Always check data types before generating pivots
- Reuse source data: Generate multiple pivot views from the same validated DataFrame
- Computed columns: Add derived columns (month, year) before pivoting
- Multi-dimensional grouping: Group by multiple columns to create cross-tabulations
- Separate output sheets: Export each pivot to its own sheet for clarity
- Summary dashboard: Create a dashboard sheet with key metrics alongside pivots
- Preserve order: Use
.groupBy()which preserves insertion order for dimensions
See groupBy Guide for more aggregation patterns and pivot Guide for advanced pivot operations.