テーブル(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

メンバ数値意味
TotalsCalculationNone0集計なし
TotalsCalculationSum1合計
TotalsCalculationAverage2平均
TotalsCalculationCount3件数
TotalsCalculationCountNums4数値の個数
TotalsCalculationMin / TotalsCalculationMax5 / 6最小 / 最大
TotalsCalculationStdDev / TotalsCalculationVar7 / 8標準偏差 / 分散
TotalsCalculationCustom9カスタム数式(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

解説

注意点

関連