Formula Chain
Jspreadsheet keeps a dependency graph of the formulas: each cell knows the formulas that read it, so a change recalculates exactly the affected cells, in dependency order. Version 13 exposes that graph through two worksheet methods.
What's new with Version 13
getDependents(cell): the formulas that read a cell, resolved the way a recalculation resolves them: direct references, ranges, full columns and rows, defined names,INDIRECTand references from other worksheetsgetPrecedents(cell): the references a formula reads, without absolute markers and with defined names resolved to their range- Sparse storage: the row records are created on demand and reached through
getCellRowObject(y); therecords[y][x]matrix of version 12 no longer exists
Overview
Consider these cells: A1 = 10, B1 = =A1, C1 = =B1 * 10, D1 = =SUM(A1:B1)
| Cell | Precedents | Dependents |
|---|---|---|
| A1 | none | B1, D1 |
| B1 | A1 | C1, D1 |
| C1 | B1 | none |
| D1 | A1:B1 | none |
When A1 changes to 20: B1 recalculates to 20, D1 to 22 and C1 to 200.
Documentation
Methods
| Method | Description |
|---|---|
getDependents |
The formulas that read a cell: its direct dependents as cell names. A dependent on another worksheet is qualified with the worksheet name, quoted when needed, in the form the core writes into references ('MY SHEET'!A1).getDependents(cell: String) => String[] |
getPrecedents |
The references a formula reads, as written in the expression with the absolute markers removed: cells, ranges, full columns and rows, worksheet qualified references and 3D spans. A defined name is resolved to its range. An empty list for a cell without a formula.getPrecedents(cell: String) => String[] |
Both methods read the graph the engine maintains, so they follow every edit: a formula replaced by a value leaves the dependents of the cells it read, a new formula joins them, and undo restores the previous graph.
Examples
Precedents and dependents of a cell
const worksheet = jspreadsheet(document.getElementById('spreadsheet'), {
worksheets: [{
data: [
[10, '=A1', '=B1*10', '=SUM(A1:B1)'],
],
}]
})[0];
worksheet.getDependents('A1'); // ['B1', 'D1']
worksheet.getPrecedents('D1'); // ['A1:B1']
worksheet.getPrecedents('A1'); // []
Every cell affected by a change
getDependents returns the direct dependents. Follow them to reach every formula a change recalculates:
const getAllDependents = function(worksheet, cell) {
const result = [];
const visited = new Set([cell]);
const stack = [cell];
while (stack.length) {
const dependents = worksheet.getDependents(stack.pop());
for (const name of dependents) {
if (! visited.has(name)) {
visited.add(name);
result.push(name);
// Dependents of another worksheet carry its name: resolve
// the worksheet before following them
if (name.indexOf('!') === -1) {
stack.push(name);
}
}
}
}
return result;
}
getAllDependents(worksheet, 'A1'); // ['B1', 'D1', 'C1']
The graph of a worksheet
The two methods together give the edges of the dependency graph, ready for a diagram library or a trace precedents feature:
const edges = [];
const data = worksheet.getData();
for (let y = 0; y < data.length; y++) {
for (let x = 0; x < data[y].length; x++) {
const cell = jspreadsheet.helpers.getCellNameFromCoords(x, y);
for (const reference of worksheet.getPrecedents(cell)) {
edges.push({ from: reference, to: cell });
}
}
}