スクリプトタスクでテーブル型データ項目を操作する

スクリプトタスクでは、テーブル型データ項目の読み書きができます。テーブル型は文字型や数値型と操作方法が大きく異なるため、この記事では基本的な操作方法をサンプルコードとともに解説します。

各データ型のメソッド詳細については、リファレンス「R2301: スクリプトタスクでのデータ取得/更新」を参照してください。


テーブル型が他のデータ型と異なる点

文字型や数値型は engine.findDataByVarName() で取得した値をそのまま engine.setDataByVarName() で書き戻せますが、テーブル型の更新では、空の場合に新しいテーブルを作成する都合上、データ定義オブジェクトを経由する手順をおすすめします(後述)。

セル単位のアクセスは、列のデータ型に応じた型で行うのが基本です。文字型の列は String、数値型の列は BigDecimal、選択型は ItemView、日付型は AddableDate として読み書きできます。通常のデータ項目と同じ感覚で扱えます。

セルを すべて文字列として扱う 補助 API(get() / put())もあります。CSV ライクな処理など、型を気にせず文字列として扱いたい場合に便利です。記事の終盤で紹介します。


テーブル型を「参照」するには

engine.findDataByVarName() でテーブルを取得すると、ScriptListArray オブジェクトが返されます。行数は size()、各セルの値は getObject(行番号, 列の指定) で取得できます。

列の指定には列番号(0 始まり)またはフィールド名が使えます。

const table = engine.findDataByVarName("q_order_items");  // テーブル型

// テーブルが空の場合は null になる
if (table === null) {
   throw new Error("明細が入力されていません");
 }

const rowCount = table.size();  // 行数

for (let i = 0; i < rowCount; i++) {
   const itemName  = table.getObject(i, "item_name");   // 文字型列 → String
    const quantity  = table.getObject(i, "quantity");    // 数値型列 → BigDecimal
   const unitPrice = table.getObject(i, 2);             // 列番号でも指定可(0 始まり)

   // 必須でない列は、セル単位で未入力(null)の場合がある
    if (quantity === null || unitPrice === null) {
        continue;  // この行はスキップ、など
   }
}

2 段階の null チェック
テーブル全体の未入力(table === null)と、行内の個別セルの未入力(getObject() が null)は別物です。table が非 null でも、行の中に値が入っていない列は null になります。必須でない列を使う場合は、行内でも null チェックを入れてください。

なお get()(文字列 API)の場合、空セルは null ではなく 空文字 "" が返ります。

テーブル型を「更新」するには

テーブル型の更新では、空の場合に新しい ScriptListArray を作る必要があるため、データ定義オブジェクトを取得しておくと一貫して書けます。

// データ定義を取得してからテーブルを取得する
const def   = engine.findDataDefinitionByVarName("q_order_items");
let   table = engine.findData(def);

// テーブルが空の場合は新規作成する
if (table === null) {
    table = def.createListArray();
}

// --- ここで行の操作を行う(後述)---

// 最後に書き戻す
engine.setData(def, table);

書き戻しは engine.setData(def, table) でも engine.setDataByVarName("q_order_items", table) でも構いません。ただし空の場合に def.createListArray() を呼ぶ必要があるため、結局 def を取得することになります。

行を追加する

table.addRow() で末尾に新しい行を追加し、row.setObject(フィールド名, 値) でセルに値を入れます。値は 列のデータ型に応じたオブジェクトを渡します。

const def   = engine.findDataDefinitionByVarName("q_order_items");
let   table = engine.findData(def);
if (table === null) {
    table = def.createListArray();
}

// 末尾に行を追加
const row = table.addRow();
row.setObject("item_name",  "ボールペン");                            // 文字型 → String
row.setObject("quantity",   new java.math.BigDecimal(10));            // 数値型 → BigDecimal
row.setObject("unit_price", new java.math.BigDecimal("120"));
// 追加直後の各セルは空文字なので、値を入れなくてもエラーにはならない

engine.setData(def, table);

setObject() は値と列のデータ型が合っているかチェックします。例えば数値型の列に数値として解釈できない文字列を渡したり、選択型の列に BigDecimal を渡すとエラーになります。

既存の行を変更する

table.getRow(行番号) で行オブジェクトを取得し、setObject() で値を書き換えます。

const def   = engine.findDataDefinitionByVarName("q_order_items");
let   table = engine.findData(def);
if (table === null || table.size() < 2) {
    throw new Error("2 行目が存在しません");
}

// 2行目(インデックス 1)の単価を変更する
const row = table.getRow(1);
row.setObject("unit_price", new java.math.BigDecimal("150"));

engine.setData(def, table);

行を削除する

table.removeRow(行番号) で指定した行を削除します。行番号は 0 始まりです。

const def   = engine.findDataDefinitionByVarName("q_order_items");
let   table = engine.findData(def);
if (table === null || table.size() < 2) {
    throw new Error("2 行目が存在しません");
}

table.removeRow(1);  // 2行目を削除
engine.setData(def, table);

条件に合致する行だけを削除する場合の注意
前方ループでインデックスを進めながら removeRow(i) を呼ぶと、削除のたびに後続行が前に詰まるため、次の行が読み飛ばされます。後方から走査するのが安全です。

// 単価が 0 の行をすべて削除する例
for (let i = table.size() - 1; i >= 0; i--) {
    const unitPrice = table.getObject(i, "unit_price");
    if (unitPrice !== null && unitPrice.signum() === 0) {
        table.removeRow(i);
    }
}

なお、テーブルを丸ごとクリアしたい場合は、行を 1 つずつ消すのではなく engine.setDataByVarName("q_order_items", null) で null を上書きするだけで済みます。


補足:文字列として扱うget()/put()

getObject() / setObject() の代わりに、セルをすべて文字列として扱う API もあります。

メソッド戻り値 / 受け取る値
table.get(行番号, 列の指定)列のデータ型を問わず、常に String
row.put(フィールド名, 値)値は String のみ
const table = engine.findDataByVarName("q_order_items");

for (let i = 0; i < table.size(); i++) {
    const quantity  = table.get(i, "quantity");    // 数値型でも String で返る
    const unitPrice = table.get(i, "unit_price");  // 同上
    // 数値計算するには BigDecimal への変換が必要
    const subTotal  = new java.math.BigDecimal(quantity).multiply(new java.math.BigDecimal(unitPrice));
}

CSV ライクな出力など、すべて文字列のままで処理したい場合に向きます。数値計算や日付計算をしたい場合は getObject() / setObject() を使うほうが簡潔です。


実践例:明細の合計金額を計算して文字型に書き込む

注文明細テーブル(q_order_items)の数量・単価から合計金額を計算し、合計金額フィールド(文字型)に書き込む例です。数値列の値は getObject() で BigDecimal として直接取得できます。

const table = engine.findDataByVarName("q_order_items");

if (table === null || table.size() === 0) {
    throw new Error("明細が入力されていません");
}

let total = new java.math.BigDecimal(0);

for (let i = 0; i < table.size(); i++) {
    const quantity  = table.getObject(i, "quantity");    // BigDecimal
    const unitPrice = table.getObject(i, "unit_price");  // BigDecimal
    // 数量・単価のいずれかが未入力の行はエラーにする(仕様に応じて continue でスキップしてもよい)
    if (quantity === null || unitPrice === null) {
        throw new Error((i + 1) + " 行目の数量または単価が未入力です");
    }
    total = total.add(quantity.multiply(unitPrice));
}

// 合計を桁区切り付きで文字型に書き込む
const formatter = new java.text.DecimalFormat("#,##0");
engine.setDataByVarName("q_total_amount", formatter.format(total) + " 円");

関連リファレンス

Questetra Supportをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む