ちょっとした集計などで使うデータの形式はjsonが多いが、たまにツール側の事情でCSVが欲しくなるときもある
具体的に言うと、データポータルにアップロードして単発で使いたい場合はCSVしかサポートされていないのでCSVが必要だったりする
さくっと変換できると何かと楽なのでメモとして残しておく
例として次のようなよくありそうなフォーマットで行ってみる
- members.json
[ { "name": "hoge", "post": "manager", "month": "2022-01-01" }, { "name": "hoge", "post": "manager", "month": "2021-12-01" }, { "name": "hoge", "post": "member", "month": "2021-11-01" }, { "name": "fuga", "post": "member", "month": "2022-01-01" }, { "name": "fuga", "post": "member", "month": "2021-12-01" }, { "name": "fuga", "post": "member", "month": "2021-11-01" } ]
処理の流れは下記
- 外側の配列を展開して各行を配列として定義し直す
- パイプで配列に対して
@csv
を通す
ヘッダなし
$ cat members.json| jq -r '.[]|[.name,.post,.month]|@csv' "hoge","manager","2022-01-01" "hoge","manager","2021-12-01" "hoge","member","2021-11-01" "fuga","member","2022-01-01" "fuga","member","2021-12-01" "fuga","member","2021-11-01"
-r
オプション
-r
で出力しないと行自体にもクオートが付与されてしまう
cat members.json| jq '.[]|[.name,.post,.month]|@csv' "\"hoge\",\"manager\",\"2022-01-01\"" "\"hoge\",\"manager\",\"2021-12-01\"" "\"hoge\",\"member\",\"2021-11-01\"" "\"fuga\",\"member\",\"2022-01-01\"" "\"fuga\",\"member\",\"2021-12-01\"" "\"fuga\",\"member\",\"2021-11-01\""
-r output raw strings, not JSON texts;
JSONテキストではなく生文字列を出力する
CSV以外の使い方だと、jsonの中の特定の値だけ取り出して次のシェルにパイプで渡すパターンが考えられる
ヘッダあり
$ cat members.json| jq -r '["name","post","month"],(.[]|[.name,.post,.month])|@csv' "name","post","month" "hoge","manager","2022-01-01" "hoge","manager","2021-12-01" "hoge","member","2021-11-01" "fuga","member","2022-01-01" "fuga","member","2021-12-01" "fuga","member","2021-11-01"
ヘッダが欲しい場合はファイルの中身をjqでよしなにする前にヘッダ用の配列を作っておきカンマでつなげる
ヘッダを動的にしたい
扱うカラム数が多い場合、今までの手法だとカラム数分だけヘッダと値を書く必要がある
サンプル程度の数だったら苦にならないが数が多ければ多いほど苦になる
そこで、下記のようにすることで解決できる
$ cat members.json | jq -r '(.[0]|to_entries|map(.key)),(.[]|[.[]])|@csv' "name","post","month" "hoge","manager","2022-01-01" "hoge","manager","2021-12-01" "hoge","member","2021-11-01" "fuga","member","2022-01-01" "fuga","member","2021-12-01" "fuga","member","2021-11-01"
- ヘッダ行
(.[0]|to_entries|map(.key))
1行目のキーを取ってきてヘッダ用に整形
最初keys
通せばよいだけでは? と思って試してみたが順序が担保できない模様
なのでto_entries
を挟む必要があるみたい
$ cat members.json| jq '.[0]|keys' [ "month", "name", "post" ] $ cat members.json| jq '.[0]|to_entries|map(.key)' [ "name", "post", "month" ]
- 2行目以降
(.[]|[.[]])
2行目以降は配列を展開してさらに展開したものを配列で包む
2個目の.[]
の挙動を知らなかった、RubyでいうHash.values
みたいな感じ
$ cat members.json| jq '.[0]' { "name": "hoge", "post": "manager", "month": "2022-01-01" } $ cat members.json| jq '.[0]|.[]' "hoge" "manager" "2022-01-01" $ cat members.json| jq '.[0]|[.[]]' [ "hoge", "manager", "2022-01-01" ]
感想
CSVを扱うときのメモの予定だったが他にも使えそうな手法を試すことができて勉強になった
参考
json - How to add a header to CSV export in jq? - Stack Overflow