代入
jqの代入は、ほとんどのプログラミング言語と少し異なります。jqは、参照とコピーを区別しません。2つのオブジェクトや配列は、等しいか等しくないかのどちらかであり、「同じオブジェクト」か「別のオブジェクト」かという追加の概念はありません。
オブジェクトに、配列である2つのフィールド .foo と .bar があり、.foo に何かを追加しても、.bar は大きくなりません。以前に .bar = .foo と設定していても同じです。Python、Java、Ruby、JavaScriptなどの言語に慣れている場合は、jqが代入前にすべてのオブジェクトを完全にディープコピーするかのように考えられます。性能のため、実際にはそうしていませんが、大まかな考え方はそのようになります。
このため、jqで循環する値を構築することはできません。たとえば、最初の要素が自分自身である配列は作れません。これは意図的なもので、jqプログラムが生成できるものをすべてJSONで表現できるようにしています。
jqのすべての代入演算子では、左辺(LHS)がパス式です。右辺(RHS)は、左辺のパス式が指定するパスへ設定する値を提供します。
jqの値は、常に不変です。内部では、集約処理を使って、. を置き換える新しい値を計算することで代入を行います。その値には、必要な代入をすべて . に適用してから、変更した値を出力します。次の例で分かりやすくなるかもしれません。{a:{b:{c:1}}} | (.a.b|=3), . は、{"a":{"b":3}} と {"a":{"b":{"c":1}}} を出力します。最後の部分式 . が見るのは、変更した値ではなく、元の値だからです。
多くの場合、|= や += のような変更を行う代入演算子を使い、= は使わない方がよいでしょう。
代入演算子の左辺は、. 内の値を参照することに注意してください。そのため、$var.foo = 1 は期待どおりには動きません。$var.foo は、. 内の有効または有用なパス式ではありません。代わりに $var | .foo =
1 を使います。
また、.a,.b=0 は .a と .b の両方を設定するわけではありませんが、(.a,.b)=0 は両方を設定します。
更新代入:|=
これは「更新」演算子 |= です。右辺にフィルターを取り、代入対象の . のプロパティの古い値を、この式に渡して処理することで、新しい値を計算します。たとえば、(.foo, .bar) |= .+1 は、foo フィールドを入力の foo に1を加えた値に、bar フィールドを入力の bar に1を加えた値に設定したオブジェクトを構築します。
左辺には、一般的なパス式を使えます。path() を参照してください。
|= の左辺が参照するのは、. 内の値であることに注意してください。そのため、$var.foo |= . + 1 は期待どおりには動きません。$var.foo は、. 内の有効または有用なパス式ではありません。代わりに、$var |
.foo |= . + 1 を使います。
右辺が何も出力しない場合(つまり、empty の場合)、del(path) と同様に、左辺のパスを削除します。
右辺が複数の値を出力する場合、最初の値だけを使います。互換性に関する注意:jq 1.5以前のリリースでは、最後の値だけを使っていました。
実行例 1
コマンド
jq '(..|select(type=="boolean")) |= if . then 1 else 0 end'入力
[true,false,[5,true,[true,[false]],false]]出力 1
[1,0,[5,1,[1,[0]],0]]算術更新代入:+=, -=, *=, /=, %=, //=
jqには、a op= b という形式の演算子がいくつかあり、すべて a |= . op b と同じです。そのため、+= 1 は、|= . + 1 と同じ意味で、値を1増やすために使えます。
実行例 1
コマンド
jq '.foo += 1'入力
{"foo": 42}出力 1
{"foo": 43}通常の代入:=
これは通常の代入演算子です。他の代入演算子と違い、右辺(RHS)への入力は、左辺(LHS)のパスにある値ではなく、左辺への入力と同じです。また、右辺が出力するすべての値を使います。以下の例を参照してください。
= の右辺が複数の値を生成する場合、jqはそれぞれの値に対して、左辺のパスをその値に設定し、変更した . を出力します。たとえば、(.a,.b) = range(2) は、{"a":0,"b":0} を出力し、その後に {"a":1,"b":1} を出力します。前述の「更新」形式の代入では、このようにはなりません。
次の例で、= と |= の違いが分かるでしょう。
入力 {"a": {"b": 10}, "b": 20} を、次のプログラムに与えます。
.a = .b
および、
.a |= .b
前者は、入力の a フィールドを入力の b フィールドに設定し、{"a": 20, "b": 20} を出力します。後者は、入力の a フィールドを、a フィールド内の b フィールドに設定し、{"a": 10, "b": 20} を出力します。
実行例 1
コマンド
jq '.a = .b'入力
{"a": {"b": 10}, "b": 20}出力 1
{"a":20,"b":20}実行例 2
コマンド
jq '.a |= .b'入力
{"a": {"b": 10}, "b": 20}出力 1
{"a":10,"b":20}実行例 3
コマンド
jq '(.a, .b) = range(3)'入力
null出力 1
{"a":0,"b":0}出力 2
{"a":1,"b":1}出力 3
{"a":2,"b":2}実行例 4
コマンド
jq '(.a, .b) |= range(3)'入力
null出力 1
{"a":0,"b":0}複雑な代入
jqの代入の左辺には、ほとんどの言語よりも、多くのものを指定できます。左辺の単純なフィールドへのアクセスはすでに見ました。配列へのアクセスも、同じように使えます。
.posts[0].title = "JQ Manual"
意外かもしれませんが、左側の式は、入力文書の異なる位置を参照する複数の結果を生成できます。
.posts[].comments |= . + ["this is great"]
この例は、入力内の各記事の"comments"配列に、文字列"this is great"を追加します。入力は、記事の配列である"posts"フィールドを持つオブジェクトです。
jqが'a = b'のような代入に出会うと、aを実行するときに、入力文書の一部を選択するためにたどった「パス」を記録します。代入を実行するときには、このパスを使って、入力のどの部分を変更するかを見つけます。等号の左辺には、どのフィルターでも使えます。そのフィルターが入力から選択するパスが、代入を行う場所になります。
これは非常に強力な操作です。前述の"blog"入力を使って、ブログ記事にコメントを追加したいとします。今回は、"stedolan"が書いた記事だけにコメントしたいとします。前述の"select"関数で、そのような記事を見つけられます。
.posts[] | select(.author == "stedolan")
この操作が提供するパスは、"stedolan"が書いた各記事を指しています。以前と同じ方法で、それぞれにコメントできます。
(.posts[] | select(.author == "stedolan") | .comments) |=
. + ["terrible."]