LoginSignup
1
0

jqのむだづかいー最大値篇

Last updated at Posted at 2022-07-29

JSON処理のコマンドラインツールであるjqは、JSONテキストを1行でちゃくっと解析、変換するときに使うものです。複雑なことをするときは、sedawkなど他のユーティリティを併用するものです。

とは言え、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;
}

参考

image.png

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0