コマンドラインJSONプロセッサー jqの演算子と関数

演算子

いくつかのjq演算子(例えば+)は与えられる引数によって(配列、数字など)異なる動作をする。
しかしながらjqは暗黙的な型変換をしません。もしもオブジェクトに文字列を追加しようとしたなら、エラーメッセージが表示され、結果は返りません。

+
+演算子は2つのフィルタを取って両方に同じインプットを適用し、その結果を足す。
「足すこと」が何を意味するかは引数の型に依存する。

  1. 数字を足すと普通に足し算をする。
  2. 配列を足すと2つの配列を結合し、1つの大きな配列とする。
  3. 文字列を足すと2つの文字列を結合し、1つの大きな文字列とする。
  4. オブジェクトを足すと、マージが行われる。(両方のオブジェクトの全てのキーと値のペアを挿入して結合したオブジェクトを作る。)

nullは全ての値に足すことができる。そしてnullではない方の値を返す。

jq '.a + 1'
   {"a": 7}
=> 8

jq '.a + .b'
   {"a": [1,2], "b": [3,4]}
=> [1,2,3,4]

jq '.a + null'
   {"a": 1}
=> 1

jq '.a + 1'
   {}
=> 1

jq '{a: 1} + {b: 2} + {c: 3} + {a: 42}'
   null
=> {"a": 42, "b": 2, "c": 3}
数字に対して普通の算数と同じように引き算をする。
-演算子を配列に使うと2つ目の配列に現れる要素を1つ目の配列から取り除く。

jq '4 - .a'
   {"a":3}
=> 1

jq '. - ["xml", "yaml"]'
   ["xml", "yaml", "json"]
=> ["json"]
*, /, %
これらの演算子は数字に対して働き、予想通りの動きをする。
文字列の乗算は指定の回数分文字列を結合する。
文字列への除算は1つ目の文字列を2つ目の文字列で分割する。
オブジェクトへの乗算は再帰的なマージを行う。これは+(加算)のに似ているが、両方のオブジェクトに同じキーがありその値がオブジェクトなら、その2つのオブジェクトは同じようにマージされる。

jq '10 / . * 3'
   5
=> 6

jq '. / ", "'
   "a, b,c,d, e"
=> ["a","b,c,d","e"]

jq '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}'
   null
=> {"k": {"a": 0, "b": 2, "c": 3}}

関数

length
組み込み関数lengthはそれぞれの型の長さを取得する。

  1. 文字列の長さは、その文字列に含まれるユニコード符号の数である。(文字列が純粋なASCIIコードならば、JSONエンコードされた文字列の長さと同じになる)
  2. 配列の長さは要素の数である。
  3. オブジェクトの長さはキーと値のペアの数である。
  4. nullの長さは0である。
jq '.[] | length' [[1,2], "string", {"a":2}, null] => 2, 6, 1, 0
keys
組み込み関数keysをオブジェクトを与えるとそのオブジェクトのキーを解列で返す。
キーはユニコード符号に従って”アルファベット順”に並べ替えられる。これは一般的な言語における基本的な並び順ではありませんが、同じキーセットを持つ2つのオブジェクトに対しローカルの設定に関わらず同じ値を返す事を期待できる。
keysに配列が与えられた場合、配列の正しいインデックス(0から配列の長さ-1)を返す。

jq 'keys'
   {"abc": 1, "abcd": 2, "Foo": 3}
=> ["Foo", "abc", "abcd"]

jq 'keys'
   [42,3,35]
=> [0,1,2]
has
組み込み関数hasはインプットオブジェクトに与えられたキーが存在するかを返す。
あるいはインプット配列に与えられたインデックスのエレメントが存在するかを返す。
gas($key)は$keyが配列のメンバーかどうかをチェックするのと同じ意味で、hasのほうがより高速である。

jq 'map(has("foo"))'
   [{"foo": 42}, {}]
=> [true, false]

jq 'map(has(2))'
   [[0,1], ["a","b","c"]]
=> [false, true]
del
組み込み関数delはオブジェクトから1つのキーとそれに対応する値を削除する。

jq 'del(.foo)'
   [{"foo": 42, "bar": 9001, "baz": 42}]
=> {"bar": 9001, "baz": 42}

jq 'del(.[1, 2])'
   [["foo", "bar", "baz"]]
=> ["foo"]
to_entries, from_entries, with_entries
これらの関数はオブジェクトと配列間のキーと値のペアを変換する。
to_entriesにオブジェクトが渡されたらインプットの各キー: 値を{“key”:k, “value”: v}とした配列をアウトプットする。
from_entriesは逆の変換をする。そしてwith_entries(foo)は’to_entries | map(foo) | from_entries’の短い書き方で、オブジェクトの全てのキーと値に何らかの処理をするときに便利である。

jq 'to_entries'
   {"a": 1, "b": 2}
=> [{"key":"a", "value":1}, {"key":"b", "value":2}]

jq 'from_entries'
   [{"key":"a", "value":1}, {"key":"b", "value":2}]
=> {"a": 1, "b": 2}

jq 'with_entries(.key |= "KEY_" + .)'
   {"a": 1, "b": 2}
=> {"KEY_a": 1, "KEY_b": 2}
select
select(foo)はfooがtrueを返す時にそのインプットをそのまま生成し、そうてないときは何も生成しません。
これはリストをフィルタリングするときに便利である。

jq 'map(select(. >= 2))'
   [1,5,3,0,7]
=> [5,3,7]
arrays, objects, iterables, booleans, numbers, strings, nulls, values, scalars
これらの関数はそれぞれ配列, オブジェクト, イテレータブル(配列またはオブジェクト), ブーリアン, 数値, 文字列, null, 値, イテレータブルではない値を選択する。

jq '.[]|numbers'
   [[],{},1,"foo",null,true,false]
=> 1
empty
emptyは全く何も結果を返しません。nullさえ返しません。
時には役立つ。あなたに必要かどうかわかりませんが。:)

jq '1, empty, 2'
   null
=> 1, 2

jq '[1,2,empty,3]'
   null
=> [1,2,3]
map(x)
map(x)はフィルタxをインプット配列の各要素に実行し、新しい配列を生成する。
map(.+1)は配列の各要素をインクリメントする。
map(x)は [ .[] | x ] と等価である。実際にそう定義されている。

jq 'map(.+1)'
   [1,2,3]
=> [2,3,4]
paths
インプットの全ての要素のパスをアウトプットする。
pathsは次の定義に相当する。

def paths: path(recurse(if (type|. == "array" or . == "object") then .[] else empty end))|select(length > 0);
jq '[paths]'
   [1,[[],{"a":2}]]
=> [[0],[1],[1,0],[1,1],[1,1,"a"]]
leaf_paths
全ての配列ではない要素、オブジェクトではない要素のパスをアウトプットする。

jq '[leaf_paths]'
   [1,[[],{"a":2}]]
=> [[0],[1,1,"a"]]
add
addフィルタをインプット配列として受け取り、要素を結合する。
これは要素の方によって合計、文字列結合、マージを意味する。(ルールは+演算子と同じである)
インプットが空の配列ならaddはnullを返す。

jq 'add'
   ["a","b","c"]
=> "abc"

jq 'add'
   [1, 2, 3]
=> 6

jq 'add'
   []
=> null
any
anyフィルタはブーリアン要素を持つインプットを受け取り、要素の中にどれか1つでもtrueがあればtrueを返す。
インプットが空の配列ならanyはtrueを返す。
jq 'all'
   [true, false]
=> false

jq 'all'
   [true, true]
=> true

jq 'all'
   []
=> true
range
range関数は数値の範囲を返す。range(4;10)は4から9までの6つの数値を返す。
数値は別々のアウトプットになる。[range(4;10)] はその範囲のインデックスをもつ1つの配列になる。

jq 'range(2;4)'
   null
=> 2, 3

jq '[range(2;4)]'
   null
=> [2,3]
floor
floor関数は数値インプットの床(繰り下げ)を返す。

jq 'floor'
   3.14159
=> 3
sqrt
sqrt関数は数値インプットの平方根を返す。

jq 'sqrt'
   9
=> 3
tonumber
tonumber関数はインプットを数値としてパースする。正しい書式の文字列を対応する数値に変換する。数値だけを返し、それ以外のインプットにはエラーになる。

jq '.[] | tonumber'
   [1, "1"]
=> 1, 1
tostring
tostring関数はインプットを文字列としてパースする。文字列は変更されず残る。そしてそれ以外のすべての値はJSONエンコードされる。

jq '.[] | tostring'
   [1, "1", [1]]
=> "1", "1", "[1]"
type
type関数は引数の型を文字列で返します。
返される型はnull, boolean, number, string, array, objectのいずれかである。

jq 'map(type)'
   [0, false, [], {}, null, "hello"]
=> ["number", "boolean", "array", "object", "null", "string"]
sort, sort_by
sort関数は配列インプットをソートする。値は以下の順番でソートされる。
・null
・false
・true
・数値
・文字列(ユニコード符号によるアルファベット順)
・配列(辞書順)
・オブジェクト

オブジェクトの並べ替えは少し複雑である。まずキーセットが配列として配列の並び順で比較され、それらのキーが等価であればキーごとに値が比較される。

sort_byはオブジェクトの特定のフィールドやソートやjqフィルタを適用したソートに使える。
.sort_by(foo)は2つの要素を各要素にfooフィルタを適用した結果で比較する。

jq 'sort'
   [8,3,null,6]
=> [null,3,6,8]

jq 'sort_by(.foo)'
   [{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]
=> [{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]

group_by
group_by(.foo)は配列のインプットを取り、同じ.fooフィールドをもつ要素のグループを個々の配列に入れる。そしてそれらすべての配列を要素として持つ大きな配列を返す。
jqの書式においてフィールドへのアクセスだけでなく.fooの代わりに使用できる。並び順はsort関数の説明と同じである。

jq 'group_by(.foo)'
   [{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]
=> [[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]
min, max, min_by, max_by
インプット配列から最小または最大の要素を探す。
_byがついたほうは特定のフィールドやプロパティを指定できす。

jq 'min'
   [5,4,2,7]
=> 2

jq 'max_by(.foo)'
   [{"foo":1, "bar":14}, {"foo":2, "bar":3}]
=> {"foo":2, "bar":3}
unique
unique関数は配列のインプットを取り同じ要素をもつ配列を生成してソートする。そして重複を取り除く。

jq 'unique'
   [1,2,5,3,5,3,1,3]
=> [1,2,3,5]
unique_by
unique_by(.foo)関数は配列の要素を取り同じ要素をもつ配列を生成してソートする。そして.fooフィールドが重複している要素を取り除く。group_byで各グループの1要素を取った配列を作ることと考えてください。

jq 'unique_by(.foo)'
   [{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]
=> [{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}]

jq 'unique_by(length)'
   ["chunky", "bacon", "kitten", "cicada", "asparagus"]
=> ["chunky", "bacon", "asparagus"]
reverse
この関数は配列を逆順にする。

jq 'reverse'
   [1,2,3,4]
=> [4,3,2,1]
contains
contains(b)フィルタはbがインプットを含んでいればtrueを返す。
文字列Bが文字列Aから切り取られたものならば、文字列Bは文字列Aに含まれている。
配列Bは配列Bの要素すべてを何処かに持っている配列Aに含まれている。
その他の型はお互いが等価の場合に含まれていると見做される。

jq 'contains("bar")'
   "foobar"
=> true

jq 'contains(["baz", "bar"])'
   ["foobar", "foobaz", "blarp"]
=> true

jq 'contains(["bazzzzz", "bar"])'
   ["foobar", "foobaz", "blarp"]
=> false

jq 'contains({foo: 12, bar: [{barp: 12}]})'
   {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}
=> true

jq 'contains({foo: 12, bar: [{barp: 15}]})'
   {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}
=> false
indices(s)
. の s がある場所のインデックスをを内容にもつ配列をアウトプットする。
インプットが配列でsも配列の場合は全ての要素がマッチするインデックスを配列でアウトプットする。

jq 'indices(", ")'
   "a,b, cd, efg, hijk"
=> [3,7,12]

jq 'indices(1)'
   [0,1,2,1,3,1,4]
=> [1,3,5]

jq 'indices([1,2])'
   [0,1,2,3,1,4,2,5,1,2,6,7]
=> [1,8]
index(s), rindex(s)
sが現れる最初のインデックス(index)または最後のインデックス(rindex)をアウトプットする。

jq 'index(", ")'
   "a,b, cd, efg, hijk"
=> 3

jq 'rindex(", ")]'
   "a,b, cd, efg, hijk"
=> 12
startswith
. が与えられた文字列の引数から始まっていたらtrueをアウトプットする。

jq '[.[]|startswith("foo")]'
   ["fo", "foo", "barfoo", "foobar", "barfoob"]
=> [false, true, false, true, false]
endwith
. が与えられた文字列の引数で終わっていたらtrueをアウトプットする。

jq '[.[]|endswith("foo")]'
   ["foobar", "barfoo"]
=> [false, true, true, false, false]
ltrimstr
. が与えられた文字列の引数で始まっていたら、インプットからその文字列を除いたものをアウトプットする。

jq '[.[]|ltrimstr("foo")]'
   ["fo", "foo", "barfoo", "foobar", "afoo"]
=> ["fo","","barfoo","bar","afoo"]
rtrimstr
. が与えられた文字列の引数で終わっていたら、インプットからその文字列を除いたものをアウトプットする。

jq '[.[]|rtrimstr("foo")]'
   ["fo", "foo", "barfoo", "foobar", "foob"]
=> ["fo","","bar","foobar","foob"]
explode
文字列のインプットを各文字の符号を格納した配列に変換する。

jq 'explode'
   "foobar"
=> [102,111,111,98,97,114]
implode
explodeの逆。

jq 'implode'
   [65, 66, 67]
=> "ABC"
split
文字列のインプットを引数のセパレータで分割する。

jq 'split(", ")'
   "a, b,c,d, e"
=> ["a","b,c,d","e"]
join
インプット配列の要素を引数のセパレータを使って結合する。splitの逆。
すなわち split(“foo”) | join(“foo”) を実行するとインプットと同じ文字列を返す。

jq 'join(", ")'
   ["a","b,c,d","e"]
=> "a, b,c,d, e"
recurse
recurse関数は再帰構造の検索を行い、全ての要素から欲しいデータを展開する。
次のインプットがファイルシステムを表しているとする。


{"name": "/", "children": [
  {"name": "/bin", "children": [
    {"name": "/bin/ls", "children": []},
    {"name": "/bin/sh", "children": []}]},
  {"name": "/home", "children": [
    {"name": "/home/stephen", "children": [
      {"name": "/home/stephen/jq", "children": []}]}]}]}

ここから全ての存在するファイルパスを展開したいとする。
.name, .children[].name, .chil-dren[].children[].name, 等等を取得しなければいけません。
こうすれば取得できる。

recurse(.children[]) | .name
jq 'recurse(.foo[])'
   {"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}
=> {"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}, {"foo":[]}, {"foo":[{"foo":[]}]}, {"foo":[]}
jq 'join(", ")'
   ["a","b,c,d","e"]
=> "a, b,c,d, e"
recurse_down
recurse(.[])の煩くないバージョン。

def recurse_down: recurse(.[]?);

と等価である。

..
recurse_downの短い書き方。これはXPathの//演算子と似せるように意図されている。
..aは動作しないことに注意。代わりに..|aを使うこと。

「コマンドラインJSONプロセッサー jqの演算子と関数」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です