Skip to main content

Two Ranges

ICellRange and IRange

SheetXL provides two range APIs designed to work together: one for interacting with the sheet, and one for manipulating values. Most applications use both — ICellRange to read from and write to the sheet, and IRange for the computation in between.

Comparison

Most developers will only work with one API — ICellRange for ui development or sheet automation, IRange for data-intensive logic or UDF authoring.

ICellRangeIRange
PurposeSheet interactionValue computation
Stateful?Yes — knows about cells, sheets, protection, transactionsNo — portable across threads, workers, and server environments
DesignImperative, like Office ScriptsFunctional/immutable, like NumPy
Primary useAI agents, macros, sheet automationData manipulation, formula authoring
UpdatesImmediate (or batched); supports undoReturns new ranges; no side effects

ICellRange - Sheet Interaction - Live Layer

ICellRange is your handle to the live sheet. Think of it like Excel's Range object — it knows where it is, what sheet it belongs to, and how to commit changes. It respects protection, participates in transactions, and integrates with undo history.

Studio (Our Interactive UI) only uses ICellRange

Modelled after 'Office Scripts' to feel natural for Excel developers.

IRange - Value Computation - Logic Layer

IRange is a stateless, immutable value object. It has no knowledge of the sheet — only data shapes, broadcasting rules, coercion, and formula semantics. Because it's stateless, the same IRange logic runs identically in a browser, a Node.js server, or a Web Worker.

Formulas and UDFs only use IRange

Modelled after 'NumPy' to feel natural to data scientists and pandas developers.

How They Work Together

ICellRange and IRange connect in a variety of ways:

  1. getValuesRange() exposes the cell range's values as an IRange.
  2. setComputedValues() is a convenience wrapper that hands you that same IRange, applies your transform, and writes the result back — all in one call.
  3. Any ICellRange method that accepts a range accepts either an ICellRange or IRange directly.
  4. UDFs (user-defined functions) work natively with IRange.
// setValues accepts an IRange directly
const computed = range.replace([[1, 2, 3]]).repeat(3, 1)
cellRange.setValues(computed)
// tiles the currently selected range
// you return a transformed IRange, and it's written back to the sheet.
getSelectedRange().setComputedValues(r => r.repeat(3, 2), { autoSelect: true, description: 'Tile Values'});

// Equivalent explicit form:
const result = cellRange.getValuesRange().replace([[1, 2, 3]]).repeat(3, 1)
cellRange.setRange(result)

Examples

The following examples show equivalent operations using each API. See the API Reference and Examples Gallery for the full list.

Setting Values Directly

// ICellRange — write a row of values to the sheet immediately
cellRange.setValues([[1, 2, 3]])

// IRange — produce a new range with those values (no sheet side effect)
const result = range.replace([[1, 2, 3]])

Broadcasting and Masking (IRange)

IRange supports NumPy-style broadcasting. repeat() repeats the values to fill the target shape; mask() applies a boolean condition to filter or gate values — similar to boolean indexing in pandas or NumPy.

// Repeat [1,2,3] across 3 rows, then apply a boolean mask
const result = range
.replace([[1, 2, 3]])
.repeat(3, 1) // repeat(rows, cols): repeat to 3×3
.mask(condition) // condition: IRange<boolean>

Sparse Updates

Use sparse updates when you need to set a small number of cells within a larger range without rewriting the whole thing.

// ICellRange — addresses are absolute cell references (e.g. 'A1')
cellRange.createSparseBuilder()
.push('A1', 2)
.push('A2', 567)
.apply() // note - apply for immediate update

// IRange — coordinates are relative to the range origin (row, col)
const updated = range.sparseBuilder()
.push(0, 0, 2) // row 0, col 0
.push(1, 0, 567) // row 1, col 0
.build() // note - build for return new range

ICellRange: uses absolute sheet addresses ('A1'). IRange: uses zero-based coordinates relative to the range's top-left corner.

Reusable Callbacks

Because IRange transforms are plain functions, they're easy to reuse — across sheets, in UDFs, or in tests. Define the transform once and apply it wherever you need it.

// A reusable IRange transform
const applyDefaults = (r: IRange): IRange =>
r.replace([[1, 2, 3]]).repeat(3, 1)

// Apply via ICellRange — transforms the cell range's IRange and writes back to sheet
cellRange.setComputedValues(applyDefaults)

// Apply to a standalone IRange — no sheet interaction
const result = range.withComputedValues(applyDefaults)

Working example

// 1. Get our handle to the LIVE sheet (ICellRange)
const priceCells = workbook.getSheet("Inventory").getRange("B2:B100");
// alternative get
// const priceCells = workbook.getSheet("Inventory").getRange("Inventory!B2:B100");

// 2. Use the "Hand-off" method
priceCells.setComputedValues((data: IRange) => {
// We are now in the Logic Layer.
// 'data' is a stateless IRange.
const taxRate = 1.10; // could use an async service if function is async
// Perform NumPy-style math
return data
.toType('number') // Ensure we're working with numbers, this will auto-coerce as appropriate
.map(v => v * taxRate) // Apply tax
.fillEmpty(0); // Clean up data gaps
});