テーブル(ListObject)とコメント — 作成・集計行・構造化参照・メモ
level: intermediate / verified: 2026-10-01 / 4言語タブ対応 / English
やりたいこと
データ範囲を Excel の「テーブル」(ListObject)にして、名前・列名・スタイル・集計行を扱う。 テーブル列を数式から参照する「構造化参照」を使い、あわせてセルへのコメント(新式スレッド)と メモ(旧式吹き出し)の追加・読み取り・削除を行う。
対象クラス
テーブルは ws.getListObjects() から、コメントは ws.getComments() から取得します。
ListObjects(テーブルのコレクション)
| メソッド | 説明 |
|---|---|
addList(name, A1C1, useFirstRowAsHeader, insertTotals) | 既存のセル範囲からテーブルを作成し、Table を返す。 |
addListFromRange(name, topA1, copyFromA1C1, useFirstRowAsHeader, insertTotals) | データを topA1 へコピーしてテーブルを作成。copyFromA1C1 は Sheet2!A1:C1 のように他シートも可。 |
getList(name) | テーブルを取得。無い場合は table is not found 例外。 |
removeList(name, deleteData?) | テーブルを削除。deleteData 既定は true(データも消える)。残すなら false。 |
Table(テーブル 1 つ)
| 分類 | メソッド |
|---|---|
| 範囲 | getAllRange() / getHeaderRowRange() / getDataBodyRange() / getTotalRowRange()(いずれも Range を返す。集計行なしで getTotalRowRange() は totalRow is not set 例外) |
| 名前 | getName() / changeTableName(name) |
| 列名 | getColumnName() / setColumnName([...])(ヘッダセルの表示も変わる) |
| スタイル | getTableStyleName() / setBuiltinStyleName(enums.XlDefaultTableStyle.Xxx, clearFormat) / setCustomTableStyleName(name, clearFormat) |
| 集計行 | isShowTotals() / setShowTotals(bool) / getTotalRowFunction(col) / setTotalRowFunction(col, enums.XlTotalsCalculation.Xxx) / setCustomRowFunction(col, formula, isArray?) / getTotalRowLabel(col) / setTotalRowLabel(col, text) |
| フィルター・表示 | getAutoFilter() / removeAutoFilter() / isShowAutoFilter() / isShowHeaders() / setShowHeaders(bool) / isShowTableStyleRowStripes() ほか |
Comments(セルコメント)
Excel にはコメントが 2 系統あります。新式(スレッド・返信・完了フラグ)と旧式メモ(吹き出し)です。
| メソッド | 系統 | 説明 |
|---|---|---|
setComment(A1, commentObject) | 新式 | 1 件追加。既にコメントがあるセルでは追加(自動で返信化)。 |
setCommentThread(A1, [commentObject...]) | 新式 | スレッドを一括設定(上書き)。先頭が本体、以降が返信。 |
getComment(A1) | 新式 | Array<dto.CommentObject> を返す。無ければ例外 comment not found。 |
removeComment(A1) | 新式 | 新式コメントを削除。 |
setMemo(A1, author, text, fontObject, visible?, rowColumnsNum?, colColumnsNum?) | 旧式 | 吹き出しメモを設定。fontObject は new dto.FontObject() で可。 |
getMemoText(A1) / getMemoAuthor(A1) | 旧式 | メモ本文 / 作者を取得。無ければ例外 memo not found。 |
removeMemo(A1) | 旧式 | メモを削除。 |
dto.CommentObject は setAuthor / setContent / setParentId(返信の親ID)/ setDone(完了フラグ)と それぞれの getter を持ちます。ID は設定すると {...} 形式の UUID が自動採番されます。
集計方法 enums.XlTotalsCalculation
| メンバ | 数値 | 意味 |
|---|---|---|
TotalsCalculationNone | 0 | 集計なし |
TotalsCalculationSum | 1 | 合計 |
TotalsCalculationAverage | 2 | 平均 |
TotalsCalculationCount | 3 | 件数 |
TotalsCalculationCountNums | 4 | 数値の個数 |
TotalsCalculationMin / TotalsCalculationMax | 5 / 6 | 最小 / 最大 |
TotalsCalculationStdDev / TotalsCalculationVar | 7 / 8 | 標準偏差 / 分散 |
TotalsCalculationCustom | 9 | カスタム数式(setCustomRowFunction で設定) |
コード
実行結果:
// tables-comments.js — テーブル(ListObject)作成・集計行・構造化参照・セルコメント
const { nodeosbxl, enums, dto } = require("nodeosbxl");
const app = new nodeosbxl.App();
const wb = app.createWorkBook("tables-comments.xlsx");
const ws = wb.openWorkSheet("Sheet1");
// ---------- データ投入(ヘッダ1行 + データ3行)----------
const rows = [
["商品名", "単価", "数量"],
["リンゴ", 120, 3],
["ミカン", 80, 5],
["ブドウ", 300, 2],
];
const values = [];
rows.forEach((row, ri) => row.forEach((cell, ci) => {
const a1 = app.convertFromRowColNumber(ri + 1, ci + 1);
const o = new dto.InputValueObject();
if (typeof cell === "number") o.setNumberValue(a1, cell);
else o.setStringValue(a1, cell);
values.push(o);
}));
ws.setValueArray(values);
// ---------- 1. テーブルを作成 ----------
const lo = ws.getListObjects();
const table = lo.addList("Sales", "A1:C4", true, true); // useFirstRowAsHeader, insertTotals
console.log("[1] テーブル作成");
console.log(` 名前 = ${table.getName()}`);
console.log(` 列名 = ${JSON.stringify(table.getColumnName())}`);
console.log(` スタイル = ${table.getTableStyleName()}`);
console.log(` 全範囲 = ${table.getAllRange().getAddress()}`);
console.log(` ヘッダ = ${table.getHeaderRowRange().getAddress()}`);
console.log(` データ部 = ${table.getDataBodyRange().getAddress()}`);
console.log(` 集計行 = ${table.getTotalRowRange().getAddress()}`);
// ---------- 2. 集計行 ----------
table.setTotalRowLabel(1, "合計");
table.setTotalRowFunction(2, enums.XlTotalsCalculation.TotalsCalculationSum);
table.setTotalRowFunction(3, enums.XlTotalsCalculation.TotalsCalculationSum);
console.log("[2] 集計行");
console.log(` ラベル(1列目) = ${JSON.stringify(table.getTotalRowLabel(1))}`);
console.log(` 数式 = ${JSON.stringify(ws.getRange("B5:C5").getFormula())}`);
console.log(` 値 = ${JSON.stringify(ws.getRange("B5:C5").getValue(true))}`);
// ---------- 3. テーブルスタイルを変更 ----------
table.setBuiltinStyleName(enums.XlDefaultTableStyle.TableStyleMedium9, true);
console.log(`[3] スタイル変更後 = ${table.getTableStyleName()}`);
// ---------- 4. 構造化参照(テーブル列を参照する数式)----------
ws.getRange("E1:E1").setFormula("SUM(Sales[単価])"); // 先頭に = は付けない
console.log("[4] 構造化参照");
console.log(` E1 数式 = ${JSON.stringify(ws.getRange("E1:E1").getFormula())}`);
console.log(` E1 値 = ${JSON.stringify(ws.getRange("E1:E1").getValue(true))}`);
// ---------- 5. コメント(新式)とメモ(旧式)----------
const cm = ws.getComments();
const mkComment = (author, content) => {
const c = new dto.CommentObject();
c.setAuthor(author);
c.setContent(content);
return c;
};
// 新式コメント(スレッド): 本体 + 返信の 2 件 → B2(save 後もファイルに残す)
cm.setCommentThread("B2", [mkComment("alice", "この数値を確認"), mkComment("bob", "確認しました")]);
// 単一の新式コメント → D2(削除の実演用)
cm.setComment("D2", mkComment("dave", "あとで消すコメント"));
// 旧式メモ(吹き出し)→ A2
cm.setMemo("A2", "Carol", "最優先商品です", new dto.FontObject());
const thread = cm.getComment("B2");
console.log("[5] コメント");
console.log(` B2 スレッド件数 = ${thread.length}`);
thread.forEach((c, i) => console.log(` [${i}] ${c.getAuthor()}: ${c.getContent()}${c.getParentId() ? " (返信)" : ""}`));
console.log(` D2 コメント件数 = ${cm.getComment("D2").length}`);
console.log(` A2 メモ = ${JSON.stringify(cm.getMemoText("A2"))} / 作者 ${JSON.stringify(cm.getMemoAuthor("A2"))}`);
// 削除: 新式は removeComment / 旧式は removeMemo(削除後の読み取りは "not found" 例外)
cm.removeComment("D2");
cm.removeMemo("A2");
const tryRead = (label, fn) => {
try { fn(); console.log(` 削除後 ${label}: 例外なし`); }
catch (e) { console.log(` 削除後 ${label}: 例外 ${e.message}`); }
};
tryRead("D2コメント", () => cm.getComment("D2"));
tryRead("A2メモ", () => cm.getMemoText("A2"));
console.log(` B2 は残すので save 後も ${cm.getComment("B2").length}件`);
const total = ws.getRange("B5:B5").getValue(true)["B5"];
const e1 = ws.getRange("E1:E1").getValue(true)["E1"];
console.log(`[確認] テーブル=${table.getName()} 集計(単価)=${total} 構造化参照(E1)=${e1}`);
wb.save();
wb.close();
[1] テーブル作成
名前 = Sales
列名 = ["商品名","単価","数量"]
スタイル = TableStyleMedium2
全範囲 = A1:C5
ヘッダ = A1:C1
データ部 = A2:C4
集計行 = A5:C5
[2] 集計行
ラベル(1列目) = "合計"
数式 = {"B5":"SUBTOTAL(109,Sales[単価])","C5":"SUBTOTAL(109,Sales[数量])"}
値 = {"B5":"500","C5":"10"}
[3] スタイル変更後 = TableStyleMedium9
[4] 構造化参照
E1 数式 = {"E1":"SUM(Sales[単価])"}
E1 値 = {"E1":"500"}
[5] コメント
B2 スレッド件数 = 2
[0] alice: この数値を確認
[1] bob: 確認しました (返信)
D2 コメント件数 = 1
A2 メモ = "最優先商品です" / 作者 "Carol"
削除後 D2コメント: 例外 comment not found
削除後 A2メモ: 例外 memo not found
B2 は残すので save 後も 2件
[確認] テーブル=Sales 集計(単価)=500 構造化参照(E1)=500
解説
- テーブルは「範囲」ではなく「名前」で扱う。
addList()の戻り値(Table)やgetList("Sales")から、ヘッダ・データ部・集計行の範囲をRangeとして取り出せます。
集計行を付けたのでgetAllRange()はA1:C5(データA1:C4+ 集計行 5 行目)になります。 - 集計行は
SUBTOTAL数式として入る。setTotalRowFunction(2, ...Sum)を設定すると、 B5 にはSUBTOTAL(109,Sales[単価])(109 = 無視しない合計)が自動生成されます。
平均はSUBTOTAL(101,...)、件数はSUBTOTAL(103,...)に対応します(実測)。
任意の数式を入れたいときはsetCustomRowFunction(col, "MAX(C2:C4)")を使い、 この場合getTotalRowFunction()はTotalsCalculationCustom(9) を返します。 - 構造化参照は「テーブル名[列名]」で書く。
SUM(Sales[単価])は 500 になります。
列名だけ(SUM(単価))では#NAME?エラーになります。
数式に先頭の=は付けません。 - 列名を変えるとテーブル内の構造化参照も追従する。
setColumnName([...])で列名を変更すると、 集計行や既存数式の中のSales[旧名]がSales[新名]に自動で置き換わります(実測)。 - コメントは 2 系統。 スレッド/返信/完了フラグを持つ新式(
setComment・setCommentThread・getComment)と、Excel 旧来の吹き出しメモ(setMemo・getMemoText)があります。
用途に合わせて使い分けます。
どちらもsave()すればファイルに永続化されます。
注意点
- テーブル名にセル参照と紛らわしい名前は使えません。
addList("A1", ...)やaddList("S2", ...)はtable name is invalid format例外になります。
同名の再作成はtable name is already exists例外です。 addList(insertTotals=true)の既定集計は全列「なし」(Excel 準拠、2026-10-01 修正)。 先頭列にラベル集計だけが自動で入り、データ列には集計関数が入りません (旧バージョンは末尾列だけCount(SUBTOTAL(103))が既定で入りました)。
集計したい列はsetTotalRowFunction()で明示します。removeList()は既定でデータも削除します。removeList("Sales")はdeleteData=true扱いで セルの値も消えます。
テーブル枠だけ外して値を残すにはremoveList("Sales", false)を使います。- 新式コメントは
dto.CommentObjectインスタンスが必須。 ドキュメントの@exampleにあるsetComment("A1", { text: "..." })のようなプレーンなリテラルはInvalid argument例外になります(実測)。
new dto.CommentObject()→setAuthor()/setContent()で組み立てて渡します。 setCommentは追加、setCommentThreadは上書き。 既にコメントのあるセルにsetCommentすると 件数が増え、新件は自動的に既存スレッドへの返信(parentId付き)になります。
スレッドを丸ごと差し替えたいときはsetCommentThreadを使います(実測)。- 無いコメント/メモを読むと例外になります。
getComment()はcomment not found、getMemoText()/getMemoAuthor()はmemo not foundを投げます(2026-10-01 修正)。
削除後の確認は try/catch で例外を捕捉して行います。 - 集計行の平均などは浮動小数点のまま返ります。
getValue(true)は表示丸め前の値を文字列で返すため、 平均SUBTOTAL(101,...)は3.3333333333333335のような値になります(表示書式で丸めてください)。
関連
- 目次
- 前: 大量データの一括投入
- 値と数式を入れて表を作る / 行・列・セル範囲の操作
- API リファレンス