2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

jqのむだづかいー表整形篇

Last updated at Posted at 2022-07-04

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

とは言え、jqにも変数定義などプログラミング言語らしき機能が備わっているので、「やればできんじゃねぇ?」と野望を募らせてしまうこともあります。

ここでは、JSONオブジェクト値の表形式への整理を試みます。具体的には、次のような同一形式のオブジェクトの配列を受け取り、

[
  {
    "name": "Penfolds Bin 389",
    "region": "South Austraria"
  },
  {
    "name": "Chateau Cantenac Brown",
    "region": "Margaux"
  },
  {
    "name": "Tenuta San Guido Le Difese",
    "region": "Bolgheri"
  }
]

オブジェクトの値で次のような表形式で出力します。

Penfolds Bin 389               South Austraria
Chateau Cantenac Brown         Margaux
Tenuta San Guido Le Difese     Bolgheri

以下、入力データはファイルtabular-sample.jsonに収容されているとします。

コード

コード(フィルタファイル)はtabular.jqで、次のGithubから取得できます(短いのでコピペでも十分ですが)。

https://github.com/stoyosawa/jqDoc-public/

フィルタファイルの改行はUnixスタイルのLFだけでなければならないので、保存時に注意してください。Windows流にCRLFだと次のエラーが報告されます。

jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?)
  at <top-level>, line 1:

フィルタファイルの使い方

これらファイルをjqから呼び出すときは、次のオプションを指定します。

  • -fオプション(--from-file)ーフィルタファイルを読み込みます。
  • -rオプション(--raw-output)ー文字列を囲む二重引用符(")を外します。

まとめると、次のようになります。

$ jq -rf tabular.jq tabular-sample.json

タブ区切り

オブジェクトの値を配列化し、それをタブ記号(\t)で連結するというのが、最も簡単な手でしょう。

$ cat tabular-sample.json | jq -r '.[] | [.name, .region] | join("\t")'

JSON全体を囲む[]の要素でループ(上記.[])し、それぞれについてnameregionの値を要素とした配列を生成し([.name, .region])、それら要素をjoin()で連結するわけです。結果は次のようになります。

Penfolds Bin 389        South Austraria
Chateau Cantenac Brown  Margaux
Tenuta San Guido Le Difese      Bolgheri

おおむねできました。しかし、左側(name)の文字列の長さに幅があるので、タブでは2列目がうまく揃いません。

スペースでパディング

こういうときは文字列をスペースでパディングし、あとから同じ長さで切り揃えます。強引ですが、sprintf()のないJavaScriptでよくやる手です。

ここでの文字列は最長で26文字なので、30文字のスペースを右側に加え(+ " "*30)て右パディングした上で、スライス([:30])で左から30文字を切り取ります。

$ cat tabular-sample.json | jq -r '.[] | [(.name + " "*30)[:30], .region] | join("")'

結果は次の通りです。

Penfolds Bin 389                South Austraria
Chateau Cantenac Brown          Margaux
Tenuta San Guido Le Difese      Bolgheri

最長文字列に合わせる

これだと、最初に最長文字を確認しなければならないので面倒です。そこで、事前に最長文字列をlengthmaxから調べ、変数$namelenに収容します。

フィルタは次の通りです。

. |
(                                                   # 変数に最大長+5を代入
    [
        .[] |
        .name |                                     # それぞれの name の
        length                                      # 長さ
    ] |
    max + 5                                         # 最大値+5
) as $namelen |                                     # $namelen に代入
.[] |                                               # あとは同じ
[
    (.name + " " * $namelen)[:$namelen],
    .region
] |
join("")

最初の.|で、入力JSONテキストをそのまま次段(2~9行目)の最大長の計算にパイプします。() as $namelenは代入式なので、その先(10行目)の.に影響は与えません。

まず、入力全体を.[]でループし、逐次.name | lengthで文字列長を取得します。このループは[](3行目と7行目)で括られているので、生成されるのはnameの文字列長の配列です。これをmax(8行目)に通すと、最大値が得られます。ここでは1列目と2列目の間に5つのスペースを加えるために、追加で+5しています。結果はas $namelenで変数に代入します。

以降は、直値の代わりに変数を用いている以外、これまでと変わりません。

実行例を示します。

$ cat tabular-sample.json | jq -rf tabular.jq
Penfolds Bin 389               South Austraria
Chateau Cantenac Brown         Margaux
Tenuta San Guido Le Difese     Bolgheri

おわりに

ええ、実用性はゼロです。Unixユーザならcolumnを使いますよね。

$ jq -r '.[] | [ .[] ] | join("|")' tabular-sample.json | column -t -s "|"
Penfolds Bin 389            South Austraria
Chateau Cantenac Brown      Margaux
Tenuta San Guido Le Difese  Bolgheri

参考

image.png

2
1
0

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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?