複数の JSON データを作成する中で、途中でオブジェクトのキーの並びが変わってしまった!
$ cat sample_a.json { "key0": "a0", "key1": "a1", "key2": "a2" } $ cat sample_b.json { "key0": "b0", "key2": "b2", "key1": "b1" }
そんなことが実際に起こり得るのかわからないですが、この場合まとめて CSV として出力 しようとすると、行によって並びが異なる状態のまま出力されてしまいます。
$ jq . *.json { "key0": "a0", "key1": "a1", "key2": "a2" } { "key0": "b0", "key2": "b2", "key1": "b1" } $ jq -r '[.[]] | @csv' *.json "a0","a1","a2" "b0","b2","b1"
キーの並びを指定する
キーの並びを指定してオブジェクトを作り直すと、いずれの行でも同じ並び順で出力することができます。
下記のように {}
の中にキーを列挙することで実現できます。
$ jq '{key0, key1, key2}' *.json { "key0": "a0", "key1": "a1", "key2": "a2" } { "key0": "b0", "key1": "b1", "key2": "b2" } $ jq -r '{key0, key1, key2} | [.[]] | @csv' *.json "a0","a1","a2" "b0","b1","b2"
なお、オブジェクトを作るときは {<key>: <value>, ...}
と書くものですが、元のオブジェクトからキー名そのままで取り出す場合は、上記のようにショートカットが使えるようです。
if the input is an object with "user", "title", "id", and "content" fields and you just want "user" and "title", you can write
{user: .user, title: .title}
Because that is so common, there's a shortcut syntax for it: {user, title}.
オブジェクトのキーをソートする
サンプルだとキーの数が連番で3つだけなので列挙するのも簡単ですが、数が多くなるとショートカットを使っても大変そうです。
to_entries
でオブジェクトをキーと値の組み合わせの配列に変換することで、ソートができるようになります。
$ jq 'to_entries' *.json [ { "key": "key0", "value": "a0" }, { "key": "key1", "value": "a1" }, { "key": "key2", "value": "a2" } ] [ { "key": "key0", "value": "b0" }, { "key": "key2", "value": "b2" }, { "key": "key1", "value": "b1" } ]
sort_by
でキーをもとにソートした後、from_entries
で再度オブジェクトに戻せます。
$ jq 'to_entries | sort_by(.key) | from_entries' *.json { "key0": "a0", "key1": "a1", "key2": "a2" } { "key0": "b0", "key1": "b1", "key2": "b2" }
sort_by
の部分を工夫してやることで、キー名以外でのソートもできるようです。
jq の --sort-keys(-S) オプションを使う
長々と書いてきましたが、jq には -S(--sort-keys)
というオプションがあり、オブジェクトのキーをソートした状態で出力することができます。
--sort-keys / -S:
Output the fields of each object with the keys in sorted order.
$ jq --sort-keys . *.json { "key0": "a0", "key1": "a1", "key2": "a2" } { "key0": "b0", "key1": "b1", "key2": "b2" }
これを使っておけば万事解決と思いきや、そう簡単には行きません。
$ jq --sort-keys -r '[.[]]' *.json [ "a0", "a1", "a2" ] [ "b0", "b2", "b1" ]
どうも、オブジェクトのキーのソートは最終的な出力に対してのみ行われるように思えます。 一旦オブジェクトのキーをソートして出力し、パイプで再度 jq コマンドに入力として渡してやる必要があります。
$ jq --sort-keys . *.json | jq -r '[.[]]' [ "a0", "a1", "a2" ] [ "b0", "b1", "b2" ]