ServiceNow開発者の皆様、GlideRecord のクエリ結果をループ(while)で配列(Array)に格納し、後からログ出力してみたら 「配列の中身がすべて同じデータ(最後に処理したレコード)に入れ替わっていた」 というトラブルに遭遇したことはありませんか? これはJavaScriptのオブジェクト参照の仕組みによる非常に有名な落とし穴です。ここでは、データが重複してしまう原因と、安全に配列へ格納するためのベストプラクティスを解説します。
なぜデータが重複(上書き)されてしまうのか?
grオブジェクトなどを配列にそのままpush()した場合、システムは値そのものではなく、メモリ上の**オブジェクトの参照先(ポインタ)**を配列に保存しますwhile(gr.next())で次のレコードに進むと、gr自体の中身が書き換わっていくため、配列内に保存されたすべての参照先データも道連れになって最新のレコードデータへと変化してしまいます
データ重複の確実な解決策
配列にデータを保存する際は、GlideRecordのオブジェクト参照をそのまま入れるのではなく、プリミティブな値(純粋な文字列など)に変換してから格納することが鉄則です。
- リストにする場合は必ず
getValue()またはgetUniqueValue()の文字列で保持する - 複数のフィールド情報を持たせたい場合は、ループ内で毎回新しいオブジェクト
{}を手動で生成し、そこにプリミティブ値をセットしてからpush()する必要があります
実装コード例(アンチパターンと推奨策)
❌ アンチパターン(全ての値が最後に上書きされる危険な例)
var incidentArr = [];
var gr = new GlideRecord('incident');
gr.query();
while (gr.next()) {
// 警告:GlideRecordオブジェクトの参照をそのまま配列に入れています。
// ループ終了時、incidentArr の中身はすべて最後に読み込まれたレコードになります。
incidentArr.push(gr);
}
✅ ベストプラクティス(新しいオブジェクトを都度生成して格納する例)
var incidentArr = [];
var gr = new GlideRecord('incident');
gr.query();
while (gr.next()) {
// 安全:ループの中で『毎回確実に新しいオブジェクト』を作成します
var incObj = {
sys_id: gr.getUniqueValue(), // システムIDを正確に取得
number: gr.getValue('number'), // 値を純粋な文字列として取得
state: gr.getDisplayValue('state') // 人間が読める表示用ステータスを取得
};
// 参照ではなく、値が固定化された新しいオブジェクトを配列に入れます
incidentArr.push(incObj);
}
// これで incidentArr の中にはレコードごとの正確なデータが重複せずに保存されます
まとめ
while(gr.next())の中でgrオブジェクト本体をそのまま配列にpush()してはいけません- データを後続の処理へ引き継ぎたい場合は、ループ内で都度
var obj = {}のように新規オブジェクトを生成する癖をつけましょう - 値を抽出する際は
gr.numberのような直接参照を避け、必ずgetValue()などのAPIを併用して文字列に変換(キャスト)することが必須です
一言まとめ: 👉 配列への格納は gr 本体を避け、常にループ内で新しい {} を作り getValue() で値を入れよう!