Extract tables from Docs into a Sheet
Pull tabular content out of a Doc into a Sheet for analysis or downstream processing.
Publié le 9 nov. 2025
Northwind’s reports arrive as Docs, and the numbers inside them arrive as tables — a pricing grid here, a headcount breakdown there. A table in a Doc looks fine, but you cannot sort it, sum it, or chart it. The moment someone wants to actually analyse those numbers, they end up retyping the table into a spreadsheet cell by cell.
This script does that transfer for you. It walks a Doc, finds every table, reads each one row by row, and writes them all into a Sheet — each table labelled and separated by a few blank rows. From there the data is real spreadsheet data: sortable, summable, ready for a formula or a chart.
What you’ll need
- A Google Doc that contains one or more tables. The script reads it by ID.
- A Google Sheet to write into. The script clears its first tab and rebuilds it, so use a dedicated Sheet rather than one with data you want to keep.
- Nothing else — no API key, no add-ons.
The script
// The Doc to read tables from — the long string from its URL.
const SOURCE_DOC_ID = '1abcDocId';
// The Sheet to write the extracted tables into.
const TARGET_SHEET_ID = '1abcSheetId';
// Blank rows left between one table block and the next.
const GAP_ROWS = 3;
/**
* Finds every table in the source Doc and writes them into the first tab
* of the target Sheet, each table labelled and spaced apart.
*/
function extractTables() {
const body = DocumentApp.openById(SOURCE_DOC_ID).getBody();
// 1. Walk the Doc and collect every table as a 2D array of cell text.
const tables = [];
for (let i = 0; i < body.getNumChildren(); i++) {
const c = body.getChild(i);
if (c.getType() !== DocumentApp.ElementType.TABLE) continue;
const table = c.asTable();
const rows = [];
for (let r = 0; r < table.getNumRows(); r++) {
const row = [];
for (let cl = 0; cl < table.getRow(r).getNumCells(); cl++) {
row.push(table.getCell(r, cl).getText());
}
rows.push(row);
}
tables.push(rows);
}
if (!tables.length) {
Logger.log('No tables found in the Doc — nothing to extract.');
return;
}
// 2. Clear the target tab so the output reflects only this run.
const sheet = SpreadsheetApp.openById(TARGET_SHEET_ID).getSheets()[0];
sheet.clear();
// 3. Write each table as a labelled block, leaving a gap between blocks.
let rowOffset = 1;
tables.forEach((t, i) => {
sheet.getRange(rowOffset, 1)
.setValue(`Table ${i + 1}`)
.setFontWeight('bold');
sheet.getRange(rowOffset + 1, 1, t.length, t[0].length).setValues(t);
rowOffset += t.length + GAP_ROWS;
});
Logger.log('Extracted ' + tables.length + ' tables into the Sheet.');
}
How it works
extractTablesopens the source Doc and loops through every child element of its body in document order.- When it finds a
TABLE, it reads it into a 2D array — one inner array per row, one string per cell — usinggetNumRows,getRow().getNumCells(), andgetCell().getText(). - It collects every table into the
tablesarray. If the Doc has no tables, it logs a message and stops. - It opens the target Sheet’s first tab and clears it, so the output always reflects the current Doc rather than stacking on old data.
- It writes each table as a block: a bold
Table Nlabel, then the table data via a singlesetValuescall. After each block it advancesrowOffsetby the table height plusGAP_ROWSblank rows, so the blocks stay visually separate.
Example run
Say the Doc contains two tables — a pricing grid and a headcount table. After a run, the Sheet’s first tab looks like this:
| A | B | C |
|---|---|---|
| Table 1 | ||
| Plan | Monthly | Annual |
| Starter | 9 | 90 |
| Pro | 29 | 290 |
| Table 2 | ||
| Team | Headcount | Lead |
| Design | 6 | Priya |
| Engineering | 11 | Sam |
Each table keeps its own shape, sits under a bold label, and is separated by blank rows — ready to sort, sum, or chart.
Run it
This is an on-demand job — run it whenever a report Doc lands:
- In the Apps Script editor, set
SOURCE_DOC_IDandTARGET_SHEET_ID, selectextractTables, and click Run. - Approve the authorisation prompt the first time.
- Open the Sheet to see the extracted tables.
Watch out for
- It clears the target tab on every run. Anything already on the first tab is wiped — point it at a dedicated Sheet, not a working one.
- Every cell comes out as text. Numbers like
29arrive as the string"29", not a number, so aSUMmay not work until you coerce them. Run a multiply-by-1 paste orVALUE()pass, or convert in code before writing. - Ragged tables can throw. The script sizes each
setValuesblock from the first row’s width. A table where rows have different cell counts — from merged cells — can mismatch and error. Tidy merged cells in the Doc first. - Nested tables are read as their text. A table inside a table cell is returned
by
getText()as a flattened string, not as its own block. Avoid nested tables in source Docs you plan to extract. - It reads tables in document order and numbers them
Table 1,Table 2, and so on. If you reorder tables in the Doc, the numbering shifts with them.
À voir aussi
Generate personalized study guides from notes
Reformat raw notes into structured study guides — for Northwind's internal training programme.
Mis à jour le 8 févr. 2026
Build a contract-clause assembly system
Construct Northwind agreements from a library of approved clauses — drag-drop in code.
Mis à jour le 1 févr. 2026
Translate and resolve Doc comments
Localise reviewer feedback on a shared Doc so multilingual teams can collaborate.
Mis à jour le 25 janv. 2026
Auto-archive finalized Docs to dated folders
File completed Northwind Docs by month so the active folder stays focused on in-flight work.
Mis à jour le 18 janv. 2026
Build a fillable intake form inside a Doc
Create structured intake forms with placeholder fields readers can fill — for client briefs.
Mis à jour le 11 janv. 2026