Closed45

『jqハンドブック』をやっていく会

mohiramohira

README

進め方

  • つまみ食いで行く
  • 3〜4回くらいで終わらせる

開催実績

  1. 2021/12/06(月) https://zenn.dev/link/comments/b642aae141f972
  2. 2021/12/13(月) https://zenn.dev/link/comments/0a9d47c8f2b0a4
  3. 2021/12/20(月) https://zenn.dev/link/comments/d05a74fbf44183
  4. 2022/01/03(月) https://zenn.dev/link/comments/92fd489fb6e847

感想

  • 個別にバラバラ読んでいく&試していくスタイルは良かった! 直列音読じゃなくてよかったと思う
  • []はイテレータという衝撃と学び
  • jqにプログラミングは求めていないですね
    • 「色付きコマンドラインJSON Viwer プラスα」という感じにおちついた
  • 「○○コマンドで良いのでは?」がめっちゃ多かったw
jnuankjnuank

感想

  • jqコマンドは大体レスポンスの色付け係の位置づけだったので、ここまで高機能なのはびっくりした
  • 逆にどういう経緯でここまでの機能を作ったのだろう?
  • ○○コマンドで良いのでは? という代替手段が出せるということは、それだけ引き出しを持っている
mohiramohira

2021/12/06(月)

やったこと

  • 1章 つまみ食い
  • 2章 つまみ食い
  • 3章 つまみ食い

MEMO

  • 「ソロで読む → 理解する → LTする」パターンが良さそう!
  • イテレータの気持ちがわかってとても嬉しい
  • 再帰が楽しい
  • なかじまさんのシェルテクニックは毎回参考になる! sed -n

次回開催メモ

  • 4章から
mohiramohira

. is 何 ?

  • . は filterに属する
  • 省略したら、補われるのか? それとも、なくても動くのか? 必須っぽいけど?
$ jq [options] filter file.json
mohiramohira
$ cat startMeUp01.json | jq '.,.,.,.'
[
  "烧餐包",
  "烧卖皇",
  "虾饺",
  "韭菜饺"
]
[
  "烧餐包",
  "烧卖皇",
  "虾饺",
  "韭菜饺"
]
[
  "烧餐包",
  "烧卖皇",
  "虾饺",
  "韭菜饺"
]
[
  "烧餐包",
  "烧卖皇",
  "虾饺",
  "韭菜饺"
]
mohiramohira
$ cat startMeUp02.json | jq '.contact'
{
  "phone": "09-123-4567",
  "メール": "booking@starcafe.com"
}
$ cat startMeUp02.json | jq '.contact.メール'
jq: error: syntax error, unexpected INVALID_CHARACTER, expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.contact.メール
jq: 1 compile error
$ cat startMeUp02.json | jq '."contact.メール"'
null
mohiramohira

1.2.5 []はただ[]を外すだけと思いきや、イテレータだったのか

# 1つの配列だった!
$ cat startMeUp01.json | jq '.'
[
  "烧餐包",
  "烧卖皇",
  "虾饺",
  "韭菜饺"
]
# 4つのJSONだったのか!
$ cat startMeUp01.json | jq '.[]'
"烧餐包"
"烧卖皇"
"虾饺"
"韭菜饺"

# 後ろに関数をつなげると、よりわかりやすい
$ cat startMeUp01.json | jq '.[] | length'
3
3
2
3
mohiramohira

イテレータパターンがとてもよくわかる

配列でもオブジェクトでも、とにかく順次取り出すってこと!

$ cat startMeUp02.json| jq '.contact'
{
  "phone": "09-123-4567",
  "メール": "booking@starcafe.com"
}

$ cat startMeUp02.json| jq '.contact[]'
"09-123-4567"
"booking@starcafe.com"
mohiramohira

leafだけ取るっぽい感じのやつ

$ cat startMeUp02.json| jq '.. | scalars'
"別不同"
false
4.5
"11:00"
"15:00"
"09-123-4567"
"booking@starcafe.com"
mohiramohira

インデントに上限あり!

$ jq --indent 17 '.' startMeUp02.json
jq: --indent takes a number between -1 and 7
Use jq --help for help with command-line options,
or see the jq manpage, or online docs  at https://stedolan.github.io/jq
mohiramohira

-c コンパクト出力で、イテレータのことをもっと理解できる

$ jq -c '.[]' startMeUp01.json
"烧餐包"
"烧卖皇"
"虾饺"
"韭菜饺"
mohiramohira
$ cat startMeUp02.json | jq '.[]'
"別不同"
false
4.5
[
  "11:00",
  "15:00"
]
{
  "phone": "09-123-4567",
  "メール": "booking@starcafe.com"
}

# コレクション型がコンパクト(1行になる) → 合計5行
$ cat startMeUp02.json | jq -c '.[]'
"別不同"
false
4.5
["11:00","15:00"]
{"phone":"09-123-4567","メール":"booking@starcafe.com"}

$ cat startMeUp02.json | jq -c -j '.[]'
別不同false4.5["11:00","15:00"]{"phone":"09-123-4567","メール":"booking@starcafe.com"}


# スカラー型は1行になるが、コレクション型は1行になるわけじゃない。そうしたいなら-cしてください
$ cat startMeUp02.json | jq -j '.[]'
別不同false4.5[
  "11:00",
  "15:00"
]{
  "phone": "09-123-4567",
  "メール": "booking@starcafe.com"
}
mohiramohira

「パイプ」違い

                    こっちのパイプはシェルの「パイプ」
                       ↓
$ cat startMeUp01.json | jq '. | join(",")'
                               ↑
                          こっちは jq の方の「パイプ」
mohiramohira

ストリームデータの処理ができるのえらい

$ printf '\036"dim sum"\n\036"yum cha"\n' | jq '.' | od -t x1c
0000000    22  64  69  6d  20  73  75  6d  22  0a  22  79  75  6d  20  63
           "   d   i   m       s   u   m   "  \n   "   y   u   m       c
0000020    68  61  22  0a
           h   a   "  \n
0000024

$ printf '\036"dim sum"\n\036"yum cha"\n' | jq --seq '.' | od -t x1c
0000000    1e  22  64  69  6d  20  73  75  6d  22  0a  1e  22  79  75  6d
         036   "   d   i   m       s   u   m   "  \n 036   "   y   u   m
0000020    20  63  68  61  22  0a
               c   h   a   "  \n
0000026
mohiramohira

jqは標準入力が前提だけど、それをNULLってことにしちゃうことで、jqの練習がいろいろできるって感じ

       o   --null-input/-n:

           Don't read any input at all! Instead, the filter is run once  using
           null  as the input. This is useful when using jq as a simple calcu-
           lator or to construct JSON data from scratch.
$ echo '{"name": "Bob", "age": 25}' | jq .
{
  "name": "Bob",
  "age": 25
}

# ちょくでかけるので、ちょっと楽かも
$ jq -n '{"name": "Bob", "age": 25}'
{
  "name": "Bob",
  "age": 25
}
mohiramohira

..(ダブルドット)楽しい

$ echo [[[[1,2,3]]]] | jq '.[0][0][0][0]'
1

$ echo [[[[1,2,3]]]] | jq '..'
[
  [
    [
      [
        1,
        2,
        3
      ]
    ]
  ]
]
[
  [
    [
      1,
      2,
      3
    ]
  ]
]
[
  [
    1,
    2,
    3
  ]
]
[
  1,
  2,
  3
]
1
2
3

$ echo [[[[1,2,3]]]] | jq -c '..'
[[[[1,2,3]]]]
[[[1,2,3]]]
[[1,2,3]]
[1,2,3]
1
2
3

$ echo [[[[1,2,3]]]] | jq  '.. | numbers'
1
2
3
jnuankjnuank
cat filter01.json | jq '[ .[]."品目"[] ] [3:8]' 
[
  "納豆巻",
  "びんちょう",
  "〆さば",
  "あじ",
  "いわし"
]


jq使わないパターン

$ cat filter01.json | jq '.[]."品目"[]' | sed -n 4,8p
"納豆巻"
"びんちょう"
"〆さば"
"あじ"
"いわし"
mohiramohira

再帰とフィルタで楽しい

$ cat filter01.json| jq '.. | strings'
"こはだ"
"とびっこ軍艦"
"とろたく軍艦"
"納豆巻"
"びんちょう"
"〆さば"
"あじ"
"いわし"
"うなぎ"
"あわび"
"中トロ"
"いくら軍艦"

# 配列にすれば、スライス演算可能
$ cat filter01.json| jq '[.. | strings][3:8]'
[
  "納豆巻",
  "びんちょう",
  "〆さば",
  "あじ",
  "いわし"
]
mohiramohira

2021/12/13(月)

やったこと

  • 4章から
  • 5章
  • 制御構造とかWHERE句的な操作でがちゃがちゃ

MEMO

  • 数値演算とかあるけど、使い所がいまいち!
  • $ jq -n '.is_created = "true"' とかでJSONをつくるのは便利かも!
  • 特定条件にマッチするオブジェクトの抽出がわからんかった!
  • FizzBuzzできた! => 完全理解!

次回開催メモ

  • 6章から
mohiramohira

関数っていうか、パイプ演算子感って感じ!

$ echo '"そばや"' | jq '. | length'
3

$ echo '"そばや"' | jq 'length'
3
# numberだけ抽出
$ cat type01.json| jq '.number'
[
  17,
  3.14,
  6.67e-11
]
$ echo '["Alice", "Bob", "Cathy"]' | jq '.[]'
"Alice"
"Bob"
"Cathy"

$ echo '["Alice", "Bob", "Cathy"]' | jq '.[] | length'
5
3
5
jnuankjnuank
$ cat type01.json| jq '.number | .[5] = .[0] + .[1]'
[
  17,
  3.14,
  6.67e-11,
  null,
  null,
  20.14
]


jnuankjnuank

numberのarrayを取った上で、イテレータで回して すべて10倍する

$ cat type01.json | jq '.number | .[] | . * 10'
170
31.400000000000002
6.67e-10


|= 更新代入

$ cat type01.json | jq '.number | .[] |= . * 10' 
[
  170,
  31.400000000000002,
  6.67e-10
]

mohiramohira
$ echo {} | jq '.is_created = "true"'
{
  "is_created": "true"
}
# pythonでいうとこういう感じってこと
>>> d = {}
>>> d['is_created'] = True
# NULL input 使ったほうがいい
$ jq -n '.is_created = "true"'
{
  "is_created": "true"
}
mohiramohira

jqを使ってJSONのサンプルデータを作る!

$ jq -n '.name="Alice" | .age=25 | .skills.programming=["Python", "Go", "C"] | .skills.cooking=["a", "b"] '
{
  "name": "Alice",
  "age": 25,
  "skills": {
    "programming": [
      "Python",
      "Go",
      "C"
    ],
    "cooking": [
      "a",
      "b"
    ]
  }
}
jnuankjnuank

dateコマンドでよいのでは

$  jq -n 'now | todateiso8601'
"2021-12-13T14:10:05Z"

mohiramohira

seqより高機能な感じがある...?

$ jq -n 'range(5)' | jq '. |= .*10'
mohiramohira

時刻に関する操作は使い所がありそう!

数値演算は、使いどころがあるのか? どういうモチベ?

mohiramohira

if文とかあるんか!?w

# tostring(.) ではないところに注意
$ cat type01.json | jq '.number[] | if . > 10 then tostring + " is Yes" else tostring + " is No" end'
"17 is Yes"
"3.14 is No"
"6.67e-11 is No"
jnuankjnuank

. で イテレータの各要素を取得できる。
tostringは .無くても勝手に解釈されるっぽい

$ cat type01.json | jq '.number[] | if . > 16 then tostring + " > 16 = true" else tostring + " > 16 = false" end'
"17 > 16 = true"
"3.14 > 16 = false"
"6.67e-11 > 16 = false"

mohiramohira

FizzBuzzできた!

$ seq 15 | jq -r '. | if . % 15 == 0 then "FizzBuzz" elif . % 5 == 0 then "Buzz" elif . % 3 == 0 then "Fizz" else . end'
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

もしくはこう

$ jq -nr 'range(16) | if . % 15 == 0 then "FizzBuzz" elif . % 5 == 0 then "Buzz" elif . % 3 == 0 then "Fizz" else . end'
mohiramohira

2021/12/20(月)

やったこと

  • 6章〜8章

MEMO

  • jqに関する印象がかなり変わった! フィルタっていうかもう、プログラミング言語って感じ。
  • なぜ、jqにいろんな機能があるんだぜ?
  • と、思ったけど、JSONのフィルタリングや変換や集計タスクを考えると、必要な機能ばっかりじゃん! すまんjq!
  • 「(俺が必要だとは思わないから)この機能いらなくね?」って思ったけど、ユーザーは俺だけじゃなかった!ww それに、別ツールで出来ちゃうってのがあるよね。

次回開催メモ

  • 9章
mohiramohira

Unicodeとかの表記

  • explode関数
  • --ascii-output, -a オプション
$ cat string01.json | jq '.[5][:5] | explode'
[
  91, # [ 0x5b
  26085, # 日 0x65e5
  26412, # 本 0x772c
  37202, # 酒 0x9152
  93 # ] 0x5d
]
$ cat string01.json | jq -a '.[5][:5]'
"[\u65e5\u672c\u9152]"

$ cat string01.json | jq --ascii-output '.[5][:5]'
"[\u65e5\u672c\u9152]"
mohiramohira

scanは使うかも

$ cat string01.json | jq '.[]'
"[White] Albert Bichot Chablis"
"[Red] albert bichot nuit-saint-georges"
"[White] Pasqua Soave Classico"
"[Red] Pasqua Chanti classico"
"[日本酒] 雨後の月 吟醸純米酒"
"[日本酒] 雨後の月 斗瓶取り"
"[日本酒] 五橋 純米吟醸 西都の雫"
"[日本酒] 五橋 斗瓶取り"
"🍷"

$ cat string01.json | jq '.[] | scan("\\w*酒\\w*")'
"日本酒"
"吟醸純米酒"
"日本酒"
"日本酒"
"日本酒"
mohiramohira

特定の配列の先頭要素と末尾要素を同時に取得できるのは便利っぽい(時刻データなら集計期間とか分かる感じ)

$ cat array01.json| jq -r '.set[0] | first, last'
ひれかつ
ロースかつ鍋
jnuankjnuank

パイプからじゃなくて、直接関数を呼ぶときはイテレータを渡す

$ cat array01.json | jq 'first(.set[0][])'
mohiramohira

index関数

$ echo [1250,1150,1450,1150] | jq '.  | index(1150)'
1

$ echo [1250,1150,1450,1150] | jq '. | rindex(1150)'
3

$ echo [1250,1150,1450,1150] | jq '. | indices(1150)'
[
  1,
  3
]
mohiramohira

flattenも使いそう

$ cat array01.json| jq '.set'
[
  [
    "ひれかつ",
    "ロースかつ",
    "海老フライ",
    "ロースかつ鍋"
  ],
  [
    "御飯",
    "定食"
  ]
]

$ cat array01.json| jq '.set | flatten'
[
  "ひれかつ",
  "ロースかつ",
  "海老フライ",
  "ロースかつ鍋",
  "御飯",
  "定食"
]
mohiramohira

combinationも使いどころありそう

$ cat array01.json| jq -c '.set'
[["ひれかつ","ロースかつ","海老フライ","ロースかつ鍋"],["御飯","定食"]]

$ cat array01.json| jq -c '.set | combinations'
["ひれかつ","御飯"]
["ひれかつ","定食"]
["ロースかつ","御飯"]
["ロースかつ","定食"]
["海老フライ","御飯"]
["海老フライ","定食"]
["ロースかつ鍋","御飯"]
["ロースかつ鍋","定食"]

$ cat array01.json| jq -c '.set | combinations | join("")'
"ひれかつ御飯"
"ひれかつ定食"
"ロースかつ御飯"
"ロースかつ定食"
"海老フライ御飯"
"海老フライ定食"
"ロースかつ鍋御飯"
"ロースかつ鍋定食"
jnuankjnuank
$ echo '[1, 2, 3]' | jq 'reduce .[] as $x (0; . = . + $x)'
6
$ echo '[1, 2, 3]' | jq -c '.[]' | xargs | tr ' ' '+' | bc
6

jqに頼らないver

$ echo '[1, 2, 3]' | tr -d "[" | tr -d "]" | tr ',' '+' | bc
mohiramohira

エントリー化

to_entries関数はプロパティを名前と値に分け、それぞれkeyとvalueというプロパティに収容して返します。オブジェクトには複数のプロパティが含まれているので、関数は複数のオブジェクトを含んだ配列を返します。関数引数はありません

この処理を「エントリー化」と呼びます。

$ jq -n '{"size": "並盛"} | to_entries'
[
  {
    "key": "size",
    "value": "並盛"
  }
]
$ jq -n '{"size": "並盛", "price":352} | to_entries'
[
  {
    "key": "size",
    "value": "並盛"
  },
  {
    "key": "price",
    "value": 352
  }
]
jnuankjnuank

再帰取得

$ cat object01.json | jq -c '.. '  
{"curry":{"name":"牛だく牛カレー","price":598,"energy":"774"},"焼肉":{"name":"牛焼肉定食","price":598,"energey":785,"sides":["rice","salad","miso soup"]},"bowl":{"name":"牛丼","price":352,"energy":635}}
{"name":"牛だく牛カレー","price":598,"energy":"774"}
"牛だく牛カレー"
598
"774"
{"name":"牛焼肉定食","price":598,"energey":785,"sides":["rice","salad","miso soup"]}
"牛焼肉定食"
598
785
["rice","salad","miso soup"]
"rice"
"salad"
"miso soup"
{"name":"牛丼","price":352,"energy":635}
"牛丼"
352
635
$ cat object01.json | jq -c '.. | keys'    
["bowl","curry","焼肉"]
["energy","name","price"]
jq: error (at <stdin>:18): string ("牛だく�...) has no keys

objectsでオブジェクトのみを取得

$ cat object01.json | jq -c '.. | objects | keys' 
["bowl","curry","焼肉"]
["energy","name","price"]
["energey","name","price","sides"]
["energy","name","price"]

mohiramohira

2022/01/03(月)

やったこと

  • 9章
  • 10章

MEMO

  • jqの守備範囲の広さ! そしてそこまでいらなくね?
  • jqじゃなくていいじゃん案件
  • そもそもjqプログラミングする気にはならない話(別言語でやっちゃうよね)

次回開催メモ

なし! 今回で終了! おつかれ!

mohiramohira

環境変数をみれるのちょっと便利じゃん! 色付きenv

$ jq -n '$ENV' 
mohiramohira

jqは入力を変形しながらパイプラインでつないでいくのが基本なので、凝った制御はめったに必要ありません。そんなプログラミングをしたくなったら、まず、既存のメカニズムで達成できないか検討しましょう。
p.209

だよね!

jqに対して「プログラミング」という感覚を抱かないよねという話。とはいえ、プログラミングできることが今回わかった(たぶん、使わないけどw)。

このスクラップは2022/01/03にクローズされました