JSON処理のコマンドラインツールであるjqは、JSONテキストを1行でちゃくっと解析、変換するときに使うものです。複雑なことをするときは、sed
やawk
など他のユーティリティを併用するものです。
とは言え、jq
にも変数定義などプログラミング言語らしき機能が備わっているので、「やればできんじゃねぇ?」と野望を募らせてしまうこともあります。
今日のお題は、次のような数値の配列を受け取り、その最大値を求めるというものです。
[1, 8, 3, 4, 10, 3, 2, 7, 5]
Bash
Bash
ならsort -nr
でしょう。邪魔な角括弧はtr
で省けます。また、行志向なsort
に合わせるため、sed
でカンマを改行に置き換えます。逆順にソートしてしまえば、あとはhead
で最初の行を取ってくるだけです。
$ echo '[1, 8, 3, 4, 10, 3, 2, 7, 5]' | tr -d '[]' | sed 's/,/\n/g' | sort -nr | head -n 1
10
jq(シンプル)
入力がJSONなので、jq
の方が簡単です。しかも、配列の最大値を取ってくるmax
関数もあります。
$ echo '[1, 8, 3, 4, 10, 3, 2, 7, 5]' | jq 'max'
10
ただ、配列要素に数値以外が含まれていると、期待外れの動きをします。型を混ぜた配列でソートすると、文字列はどんな数値よりも大きいと判断されるからです。
$ echo '[1, 8, "3", 4, 10, 3, "2", 7, 5]' | jq 'max' # 文字列表記の数字混じり
"3" # 期待外れ
こういう場合は、先にtonumber
で文字列を数値に変換します。
ただ、tonumber
は配列を受け取ってくれないので、イテレーション(.[]
)をかけて要素単位で処理します(数値はそのまま数値として出力されます)。反対に、max
は配列しか受け付けないので、イテレーションの結果を再度配列に戻さなければなりません。これは、イテレーションと数値化の部分を配列化の[]
でくくります。
$ echo '[1, 8, "3", 4, 10, 3, "2", 7, 5]' | jq '[.[] | tonumber] | max'
10
あ、でも、こう書くくらいなら高階関数なmap
の方がスタイリッシュですね。
$ echo '[1, 8, "3", 4, 10, 3, "2", 7, 5]' | jq 'map(tonumber) | max'
10
jq(あえてreduce)
オリジナルのお題は、max()
などの関数を使わず、手づからループを組め、でした。というわけで、reduce
を使います。
「関数を使うな」なのに高階関数なreduce
ではずるくないかという説もありますが、中間結果を記憶するループマシンとして使っているだけなので、許してもらいます。
$ echo '[1, 8, 3, 4, 10, 3, 2, 7, 5]' | \
jq 'reduce .[] as $x(-infinite; if $x > . then $x else . end)'
10
reduce ... as $x
は、入力された配列の要素をループしながら、逐次、変数$x
にセットします(Pythonならfor x in ...
に相当)。
第1引数はループ変数.
の初期値です。ここではその都度の最大値を収容するのに使います。最大値を探すので、最初はinfinite
の負数からスタートします。
第2引数は.
の更新式です。以降、配列要素$x
とこの.
を比較し、要素の方が大きければ、.
をその値と入れ替えます。そうでなければ、以前の最大値をそのまま保持します(.
)。if
文はif-then-else-end
の形式です。
reduce
はループを終了すると、最後の.
の値を出力します。
おわりに
最初のmax
の箇所はさておき、わざわざreduce
を使うことはないですよね。でも、そういうお題だったのです。ちなみに、オリジナルは「JavaScriptで書け」でした。こうですかね。
function maximum(arr) {
let maxx = Number.NEGATIVE_INFINITY;
arr.forEach(elem => {
if (maxx < elem) {
maxx = elem;
}
});
return maxx;
}
参考
-
./jq -
jq
のオフィシャルサイトです(英文)。 -
man page of tr - Unixコマンドの
tr
のman pageです。 -
man page of sed - Unixコマンドの
sed
のman pageです。 -
man page of sort - Unixコマンドの
tr
のman pageです。 -
man page of head - Unixコマンドの
tr
のman pageです。 -
jqハンドブックーNetOps/DevOps必携のJSONパーザ -
max
やreduce
はこちらでも紹介しています。【出版社 | honto | amazon.co.jp | ヨドバシカメラ 】。