UNPKG

hyperformula

Version:

HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas

136 lines 4.26 kB
/** * @license * Copyright (c) 2025 Handsoncode. All rights reserved. */ import { addressKey } from "../Cell.mjs"; /** * Maps top-left corner addresses to their ArrayFormulaVertex instances. * An ArrayFormulaVertex is created for formulas that output multiple values (e.g., MMULT, TRANSPOSE, array literals). * The same ArrayFormulaVertex is referenced in AddressMapping for all cells within its spill range. * ArrayFormulaVertex lifecycle: * - Prediction (ArraySizePredictor.checkArraySize) → determines if formula will produce array * - Creation → new ArrayFormulaVertex(...) added via addArrayFormulaVertex() * - Address registration → setAddressMappingForArrayFormulaVertex() sets the vertex for all cells in range * - Evaluation → computes actual values, stores in ArrayFormulaVertex.array * - Shrinking → if content placed in array area, array shrinks via shrinkArrayToCorner() */ export class ArrayMapping { constructor() { this.arrayMapping = new Map(); } getArray(range) { const array = this.getArrayByCorner(range.start); if (array === null || array === void 0 ? void 0 : array.getRange().sameAs(range)) { return array; } return; } getArrayByCorner(address) { return this.arrayMapping.get(addressKey(address)); } setArray(range, vertex) { this.arrayMapping.set(addressKey(range.start), vertex); } removeArray(range) { if (typeof range === 'string') { this.arrayMapping.delete(range); } else { this.arrayMapping.delete(addressKey(range.start)); } } count() { return this.arrayMapping.size; } *arraysInRows(rowsSpan) { for (const [mtxKey, mtx] of this.arrayMapping.entries()) { if (mtx.spansThroughSheetRows(rowsSpan.sheet, rowsSpan.rowStart, rowsSpan.rowEnd)) { yield [mtxKey, mtx]; } } } *arraysInCols(col) { for (const [mtxKey, mtx] of this.arrayMapping.entries()) { if (mtx.spansThroughSheetColumn(col.sheet, col.columnStart, col.columnEnd)) { yield [mtxKey, mtx]; } } } isFormulaArrayInRow(sheet, row) { for (const mtx of this.arrayMapping.values()) { if (mtx.spansThroughSheetRows(sheet, row)) { return true; } } return false; } isFormulaArrayInAllRows(span) { let result = true; for (const row of span.rows()) { if (!this.isFormulaArrayInRow(span.sheet, row)) { result = false; } } return result; } isFormulaArrayInColumn(sheet, column) { for (const mtx of this.arrayMapping.values()) { if (mtx.spansThroughSheetColumn(sheet, column)) { return true; } } return false; } isFormulaArrayInAllColumns(span) { let result = true; for (const col of span.columns()) { if (!this.isFormulaArrayInColumn(span.sheet, col)) { result = false; } } return result; } isFormulaArrayInRange(range) { for (const mtx of this.arrayMapping.values()) { if (mtx.getRange().doesOverlap(range)) { return true; } } return false; } isFormulaArrayAtAddress(address) { for (const mtx of this.arrayMapping.values()) { if (mtx.getRange().addressInRange(address)) { return true; } } return false; } moveArrayVerticesAfterRowByRows(sheet, row, numberOfRows) { this.updateArrayVerticesInSheet(sheet, (key, vertex) => { const range = vertex.getRange(); return row <= range.start.row ? [range.shifted(0, numberOfRows), vertex] : undefined; }); } moveArrayVerticesAfterColumnByColumns(sheet, column, numberOfColumns) { this.updateArrayVerticesInSheet(sheet, (key, vertex) => { const range = vertex.getRange(); return column <= range.start.col ? [range.shifted(numberOfColumns, 0), vertex] : undefined; }); } updateArrayVerticesInSheet(sheet, fn) { const updated = Array(); for (const [key, vertex] of this.arrayMapping.entries()) { if (vertex.sheet !== sheet) { continue; } const result = fn(key, vertex); if (result !== undefined) { this.removeArray(key); updated.push(result); } } updated.forEach(([range, array]) => { this.setArray(range, array); }); } }