今回は、IRISでJSON的な操作が可能になる%DynamicArrayについて解説します。
はじめに
%DynamicArrayは、JSONのような動的なデータ構造を扱うためのクラスです。
JSON形式のデータを柔軟に生成・操作・変換したり、外部APIとの連携でJSONデータを扱い場合にも便利です。
サンプルは全てIRIS 2023.1.2.450.0で作成しています。
今回はJSON風の配列を扱うクラス、%DynamicArrayについて解説します。
基本的な動作に関しては%DynamicObjectと同じため、共通している箇所に関しては下記記事を参照してください。
基本操作
オブジェクト生成
ClassMethod sample()
{
s name = "藤原道長"
s json = [
(name),
"男",
{
"長男": "藤原頼通",
"次男": "藤原頼宗"
},
"関白",
(2*5)
]
s json."5" = "add!!" // 配列の番号を直接指定する
d json.%Push(1,"boolean").%Push("","null").%ToJSON()
}
%DynamicArrayは、JSONのオブジェクトを模しているので、ObjectScriptとは異なり配列の番号は0から始まります。
そのため、6番目に挿入したい場合は、「s json.“5“ = “add!!」になります。
0から始まり6番目である「5」に挿入ですね。
「%Push(value, type)」は、%DynamicObjectの%Setと同様に、型を変換して配列に追加る事が可能です。
→今回は、7番目に「1」、8番目に「“”」を追加しました。
【関数の実行結果】
[
”藤原道長”,
”男”,
{“長男”:”藤原頼通”,”次男”:”藤原頼宗”},
”関白”,
10,
”add!!“,
true,
null
]
%Pushの型(type)は%Setのの型と同様の為、割愛します。
空の値
配列のキーを直接指定して値を格納する事ができるのであれば、格納されていない配列位置はどうなっているのでしょうか。
ClassMethod sample()
{
s json = []
s json."5" = "add!!" // 空の配列に対し、5番目に格納する
w json.%ToJSON(),!!
s iter = json.%GetIterator()
while iter.%GetNext(.prop, .val, .type) {
w "prop:",prop,", val:",val,", type:",type,!
}
w !,"---------------------",!
w "配列数:",json.%Size(),!
w "空の型:",json.%GetTypeOf("0")
}
【関数の実行結果】
[null,null,null,null,null,”add!!”]
prop:5, val:add!!, type:string
———————–
配列数:6
空の型:unassigned
ここで分かることは、下記になります。
- 配列の数は6個
- 1~5個までは値が入っておらず、空の型は「unassigned」
- イテレータを使用すると、格納されたデータのみ(今回は6番目)取得できる
一先ず配列分回す
ClassMethod sample()
{
s json = []
s json."3" = "add!!"
f pos=0:1:json.%Size()-1 {
w json.%Get(pos,"null!"),!
}
}
【関数の実行結果】
null!
null!
null!
add!!
値が格納されていなくても、配列分ループしたいときはforループを使えばよいと思います。
値の中身は毎回チェックする必要があります。
格納されている分だけ配列を回す
ClassMethod sample()
{
s json = []
s json."5" = "add!!"
s iter = json.%GetIterator()
while iter.%GetNext(.prop, .val, .type) {
w "prop:",prop,", val:",val,", type:",type,!
}
}
イテレータを使用した取得方法になります。
実装例
%DynamicArrayの機能を使った下記実装例を2点確認したいと思います。
- Queue機能
- Undo/Redo機能
Queue機能の実装
Queue機能は、通信処理でよく見ますね。
ClassMethod sample()
{
s json = [1,2,3,4,5]
f pos=6:1:10 {
d json.%Push(pos)
w json.%Remove(0),!
}
d json.%ToJSON()
}
「%Push」と「%Remove」の組み合わせは、キュー(Queue)機能(いわゆる後入れ/先出し)を実装する際に便利な組み合わせです。
【関数の実行結果】
1
2
3
4
5
[6,7,8,9,10]
Undo/Redo機能の実装
Undo/Redo機能は、お絵かきソフトや将棋・オセロ等のゲームでよく見ますね。
ClassMethod sample()
{
s json = [1,2,3,4,5]
f pos=6:1:10 {
d json.%Push(pos)
w json.%Pop(),!
}
d json.%ToJSON()
}
「%Push」と「%Pop」を組み合わせると、Undo/Redo機能(いわゆる後入れ/後出し)を実装するのに便利な組み合わせです。
【関数の実行結果】
6
7
8
9
10
[1,2,3,4,5]
%Listとのパフォーマンス比較
%DynamicArrayの機能と比較的近い立場にあるのが、%Listになると思います。
両者は、文字列(%List)とオブジェクト(%DynamicArray)なので、立ち位置が根本から異なりますが、順番に値を確保していく点は共通しています。
似ている様で全く立ち位置が異なりますが、今後実装を判断する基準としたいため、パフォーマンスの比較を行ってみたいと思います。
処理速度を比較するため、下記関数を用意してみました。
ClassMethod check(cnt As %Integer)
{
s list = ""
s start = $zh
f pos=1:1:cnt {
s $li(list, *+1)=pos
}
w !,"$list:",$zh - start
s json = []
s start = $zh
f pos=1:1:cnt {
d json.%Push(pos)
}
w !,"Array:",$zh - start
}
件数 | 50件 | 100件 | 300件 | 1,000件 | 5,000件 |
---|---|---|---|---|---|
$List | 0.000064 | 0.000127 | 0.000648 | 0.007341 | 0.178427 |
Array | 0.000143 | 0.000337 | 0.000696 | 0.002256 | 0.02038 |
%Listに100も200も詰め込むことがイレギュラーだとは思いますが、約300件を境に%Listと%DynamicArrayの処理速度が逆転するようです。
文字列である%List型は、やはり大量データには向いていないですね。
では、データの取り出しに関してはどうでしょうか。
下記関数を用意して計測を行いました。
ClassMethod check(cnt As %Integer)
{
s list = ""
f pos=1:1:cnt s $li(list, *+1)=pos
s start = $zh
s ptr=0
while($listNext(list, ptr, val)){ }
w !,"$list:",$zh - start
s json = []
f pos=1:1:cnt d json.%Push(pos)
s start = $zh
s iter = json.%GetIterator()
while iter.%GetNext(, .val) {}
w !,"Array:",$zh - start
}
件数 | 50件 | 100件 | 1,000件 | 5,000件 |
---|---|---|---|---|
$List | .000011 | 0.000013 | 0.00012 | 0.000353 |
Array | 0.000258 | 0.000849 | 0.004466 | 0.02124 |
項目の取り出しに関しては、件数が増えれば増えるほど%Listが早く、%DynamicArrayの差が広がります。
わずかな差ではありますが、%DynamicArrayは遅いですね。
それよりも予想以上に%Listが優秀ですね。ビックリします。
順に値を取りに行く$ListNextの性能が高いのでしょう。
おわりに
いかがだったでしょうか。
本記事では、その基本的な使い方や特性、簡単な利用例について解説しました。
%DynamicArrayは、柔軟性と操作性に優れたデータ構造であり、従来の配列やグローバル変数と比べて、特定のシナリオで効率的なデータ操作を可能にします。
特に、JSON形式との親和性が高いため、外部APIやモダンなアプリケーションとの連携においても大きな力を発揮します。
このデータ構造を活用することで、より簡潔で直感的なコードを書けるだけでなく、アプリケーション全体の保守性や拡張性を向上させることができます。
今後も%DynamicArrayを適切に活用し、開発効率を高めていきたいですね!