Bulk Input of Large Data — setValueArray / setFormulaArray
level: intermediate / verified: 2026-10-01 / tabs: Node.js・Python・Java・WebAssembly / 日本語
What you want to do
Write values and formulas into thousands or tens of thousands of cells in one shot as an array instead of calling getRange() per cell. Also, bulk-input formulas whose relative references shift row by row.
| Approach | What to use |
|---|---|
| Per-cell input | ws.getCells(r, c).setNumberValue(...) |
| Bulk value input | ws.setValueArray(Array<dto.InputValueObject>) |
| Bulk formula input | ws.setFormulaArray(Array<dto.InputFormulaObject>) |
Classes covered
dto.InputValueObject
| Method | Description |
|---|---|
setStringValue(A1C1, val, numberFormat?) | Sets a string value. |
setNumberValue(A1C1, val, forceString?, numberFormat?) | Sets a number. |
setBooleanValue(A1C1, val, forceString?, numberFormat?) | Sets a boolean. |
setDateTimeValue(A1C1, val, forceString?, numberFormat?) | Sets a date/time value. |
setEmptyVal(A1C1) | Clears the cell range. |
getA1C1() / getInputType() / getStringValue() / getNumberValue() / getBooleanValue() / getDateTimeValue() / getNumberFormat() / getForceString() | Read back the configured content. |
dto.InputFormulaObject
| Method | Description |
|---|---|
setA1C1(A1C1) | Sets the target cell. |
setFormula(formula) | Sets the formula (no leading =). |
getA1C1() / getFormula() | Read back the configured content. |
Code
Output:
// bulk-input.js — bulk input of values and formulas, speed comparison against per-cell input
const { nodeosbxl, dto } = require("nodeosbxl");
const app = new nodeosbxl.App();
const wb = app.createWorkBook("bulk-input.xlsx");
const ws = wb.openWorkSheet("Sheet1");
// ---------- 1. write mixed types in one shot ----------
const date = new dto.DateTimeObject();
date.setYMD(2024, 3, 20);
const values = [];
const addString = (a1, val) => {
const o = new dto.InputValueObject();
o.setStringValue(a1, val);
values.push(o);
};
// Trailing optional arguments can be passed as undefined (treated as omitted)
const addNumber = (a1, val, fmt) => {
const o = new dto.InputValueObject();
o.setNumberValue(a1, val, false, fmt);
values.push(o);
};
addString("A1", "Product");
addNumber("B1", 1234.5);
addNumber("C1", 0.25, "0.0%");
// Date: omitting the format auto-sets a default format (same as Range.setDateValue)
const dateVal = new dto.InputValueObject();
dateVal.setDateTimeValue("D1", date, false, "yyyy-mm-dd"); // explicit format
values.push(dateVal);
const dateVal2 = new dto.InputValueObject();
dateVal2.setDateTimeValue("D2", date); // omitted → auto
values.push(dateVal2);
ws.setValueArray(values);
console.log("[1] bulk input with mixed types");
for (const a1 of ["A1", "B1", "C1", "D1", "D2"]) {
const r = ws.getRange(`${a1}:${a1}`);
console.log(` ${a1}: display=${JSON.stringify(r.getValue()[a1])} format=${JSON.stringify(r.getNumberFormat()[a1])}`);
}
// ---------- 2. clearing cells is bulk input too ----------
ws.getRange("A2:A2").setValue("temporary entry");
const empty = new dto.InputValueObject();
empty.setEmptyVal("A2");
ws.setValueArray([empty]);
console.log("[2] after setEmptyVal, A2 =", JSON.stringify(ws.getRange("A2:A2").getValue()["A2"]));
// ---------- 3. bulk-input formulas with relative references shifted per row ----------
// Range.setFormula() does not expand relative references, so use this instead
for (let r = 1; r <= 3; r++) ws.getRange(`F${r}:F${r}`).setNumberValue(r * 10);
const formulas = [];
for (let r = 1; r <= 3; r++) {
const f = new dto.InputFormulaObject();
f.setA1C1(app.convertFromRowColNumber(r, 7)); // G1, G2, G3
f.setFormula(`F${r}*2`); // different relative reference per row
formulas.push(f);
}
ws.setFormulaArray(formulas);
console.log("[3] after setFormulaArray");
console.log(" formulas =", JSON.stringify(ws.getRange("G1:G3").getFormula()));
console.log(" values =", JSON.stringify(ws.getRange("G1:G3").getValue(true)));
// ---------- 4. speed comparison (2,000 rows x 5 cols = 10,000 cells) ----------
const ROWS = 2000, COLS = 5;
const wsCell = wb.addWorkSheet("CellByCell");
let t = Date.now();
for (let r = 1; r <= ROWS; r++) {
for (let c = 1; c <= COLS; c++) wsCell.getCells(r, c).setNumberValue(r * 100 + c);
}
const msCell = Date.now() - t;
const wsBulk = wb.addWorkSheet("Bulk");
const bulk = [];
t = Date.now();
for (let r = 1; r <= ROWS; r++) {
for (let c = 1; c <= COLS; c++) {
const o = new dto.InputValueObject();
o.setNumberValue(app.convertFromRowColNumber(r, c), r * 100 + c);
bulk.push(o);
}
}
const msBuild = Date.now() - t;
t = Date.now();
wsBulk.setValueArray(bulk);
const msApply = Date.now() - t;
console.log(`[4] writing ${ROWS * COLS} cells`);
console.log(` per-cell getCells().setNumberValue() : ${msCell} ms`);
console.log(` setValueArray (build ${msBuild} + write ${msApply}) : ${msBuild + msApply} ms`);
const a = wsCell.getRange("E2000:E2000").getValue(true)["E2000"];
const b = wsBulk.getRange("E2000:E2000").getValue(true)["E2000"];
console.log(`[check] both methods agree: E2000 = ${a === b ? a : `mismatch ${a} / ${b}`}`);
wb.save();
wb.close();
[1] bulk input with mixed types
A1: display="Product" format="General"
B1: display="1234.5" format="General"
C1: display="25.0%" format="0.0%"
D1: display="2024-03-20" format="yyyy-mm-dd"
D2: display="2024/3/20" format="yyyy/m/d;@"
[2] after setEmptyVal, A2 = ""
[3] after setFormulaArray
formulas = {"G1":"F1*2","G2":"F2*2","G3":"F3*2"}
values = {"G1":"20","G2":"40","G3":"60"}
[4] writing 10000 cells
per-cell getCells().setNumberValue() : 102 ms
setValueArray (build 18 + write 58) : 76 ms
[check] both methods agree: E2000 = 200005
The ms values in [4] change with every run (the above is one example run).
Notes
Speed
| Cells | Per-cell getCells().setNumberValue() | setValueArray() | Difference |
|---|---|---|---|
| 10,000 (2,000 rows x 5 cols) | ~110 ms | 76 ms (build 18 + write 58) | ~1.4x |
| 100,000 (20,000 rows x 5 cols) | 1,390 ms | 770 ms (build 130 + write 640) | ~1.8x |
Both rows are single measured runs on osbxl 1.5.0 / Node.js v21; results vary with the environment and between runs. As a trend, the gap widens as the cell count grows. **The time spent assembling InputValueObjects on the JavaScript side is not negligible either**, which is why the measurements above separate "build" from "write". If you need to write tens of thousands of cells or more, use setValueArray().
When to use which
setValueArray()— when you have "values to insert in bulk", such as an array, CSV, or DB query results.
You can mix types (string / number / boolean / date) in a single array.setFormulaArray()— when inserting formulas with different relative references per row.Range.setFormula()does not expand relative references (see overview caveats), so this is the right tool for building calculated columns.- For a few dozen cells,
getCells(r, c)/getRange("A1:A1")is perfectly fine — favor readability.
Caveats
- Plain object literals are not accepted. You must create instances of
dto.InputValueObject/dto.InputFormulaObject.// ✗ throws: Invalid argument ws.setValueArray([{ cell: "A1", value: 100 }]); // ✓ const o = new dto.InputValueObject(); o.setNumberValue("A1", 100); ws.setValueArray([o]); setDateTimeValue()auto-sets the display format (symmetric withRange.setDateValue()). OmittingnumberFormatautomatically appliesyyyy/m/d;@(date only) /h:mm:ss;@(time only) /yyyy/m/d h:mm:ss;@(date + time) depending on the content.
Pass the 4th argument only when you want a specific format.setDateTimeValue("D2", date) → display "2024/3/20" format "yyyy/m/d;@" setDateTimeValue("D1", date, false, "yyyy-mm-dd") → display "2024-03-20" format "yyyy-mm-dd"- When the same cell is specified multiple times, the element later in the array wins (measured). To avoid unintended overwrites, make sure there are no duplicate A1C1 values before writing.
InputFormulaObjectrequiressetA1C1(). Setting only the formula has no effect if the target cell is not specified.- No leading
=insetFormulaArray()formulas either. Usef.setFormula("F1*2"). - **
undefinedmay only be passed for *trailing* optional arguments** (treated as omitted).undefinedornullin a middle position throws anunsupported argumentsexception.This is a common specification across osbxl (including the// ✓ trailing undefined = omitted o.setNumberValue("A1", 100, false, undefined); // ✗ throws: undefined in the middle o.setNumberValue("A1", undefined, false, "#,##0"); // ✗ throws: null is not treated as omitted o.setNumberValue("A1", 100, null);Rangesetters).