[JSONata] ドット演算子について理解を深める

[JSONata] ドット演算子について理解を深める

Clock Icon2025.05.29

こんにちは。サービス開発室の武田です。

引き続き、AWS Step FunctionsのためにJSONataの勉強中です。今日は、Step Functions自体は出てきません。

今回はドット演算子(.)についてです。おそらく普段からAWSを使用している方は、jqJMESPathに馴染みのある方が多いでしょう。そういう人は「.?あぁプロパティ掘り下げるやつね」とさっと理解すると思います。私もそうでした。

しかしドキュメントを読んでみると、なんだかそんな単純なものではないようです。具体例を見ながら、このドット演算子(.)について理解を深めていきましょう。

. は Map なんだ

Mapといっても「地図」ではなくて「写像」の方です。関数型プログラミングとかでよく出てくるアレです。

次の式を例に、どういうことなのか見ていきましょう。

$map($range(1, 10, 1), function($a) { $a * $a })

これは、「「1から10までの値を持った配列」を生成し、各要素を二乗した配列を生成する」という式です。maprangeといったプログラミング言語でもお馴染みの関数がJSONataでも組込みで実装されています。JSONataではラムダ式(匿名関数)がサポートされているため、$mapの第2引数に渡しています。なおJSONataの関数はファーストクラスオブジェクトというだけでなく、部分適用関数などもサポートされており、とてもパワフルです。これもまた機会があれば取り上げていきます。

先ほどの式はきちんと意図したとおりに動きますが冗長です。少しずつリファクタリングしていきましょう。まずは$rangeを書き換えます。JSONataではRangeオペレーター(..)がサポートされており、今回はこれが使えます。次のように書き換えられます。

$map([1..10], function($a) { $a * $a })

続いて$mapを書き換えます。ここが 今回の趣旨 と言ってもいいでしょう。そう、実は$mapを使わなくても、各要素に関数を適用できるのです。書き換えると次のようになります。

[1..10].(function($a) { $a * $a }($))

私は最初なんでこれが動くのかよくわかりませんでした。しかし「. は Map」ということが理解できると腑に落ちます。JSONataのドット演算子(.)はまさしく「演算子」なんです。左オペランド([1..10])に対して右オペランド((function($a) { $a * $a }($)))を Mapする という演算子。それがドット演算子(.)です。

右オペランド内にある$は、コンテキストにアクセスするためのシンボルで、今回で言えば配列の各要素が渡されます。

さてこれが理解できたらこのエントリの目標は達成なのですが、もう少し進めましょう。先ほど$を使えば要素にアクセスできることがわかったので、二乗する処理は単に次のように書き換えられます。

[1..10].($ * $)

どうです?すごくないですか?ちなみに冪乗関数は$powerが組込みで提供されているので、同じ処理は次のようにも書けます。

[1..10].$power(2)

応用として、「ランダムな値を10個持った配列」も簡単に作れます。

[1..10].$random()

普段のプロパティアクセスはどう捉えればいいか

ドット演算子(.)がMapだということは理解できてきたでしょうか。それでは、最初に挙げた「プロパティアクセス」の挙動はどう理解すればいいでしょう。実はこれも写像のひとつとしてみなせるため、特別扱いなどは必要ありません。

まずは次の例を考えてみましょう。

(
  $o := [{ "a": 1 }, { "a": 2 }, { "b": 2 }];
  $o.a /* [1, 2] */
)

$oは3つの要素を持った配列です。$o.aは各要素の「aプロパティだけを返す写像」とみなせます。そうすると、[1, 2, nothing]ということになりますが、JSONataでは nothingは何もない 意味ですので、上記の式は結果として[1, 2]を返します。

同様に次の例も考えてみます。

(
  $o := { "a": 1 };
  $o.a /* 1 */
)

$oが配列ではなく単一のオブジェクトになりました。JSONataでは、「単一の値は、1要素の配列として扱う」ことで、この挙動をスマートに解決します。

つまり内部では、次のような処理が行われていると考えてください。

{ "a": 1 } →(1要素の配列とみなす) [{ "a": 1 }] →(Map処理を解決) [1] →(1要素なので単一の値とする) 1 というわけです。

また補足ですが、ドット演算子(.)は左結合です。そのためa.b.c(a.b).cとなるため、違和感なく使用できるわけですね。

まとめ

JSONataのドット演算子(.)はMapなんだ!というお話でした。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.