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のむだづかいー検索篇

Posted at

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

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

ここでは、次のようなオブジェクトを要素とした配列から、指定の部分文字列にNameの値がマッチするオブジェクトを抽出するjqフィルタを考えます。

[
 {
  "id": 2,
  "Name": "Cumings, Mrs. John Bradley (Florence Briggs Thayer)",
  "Sex": "female",
  "Age": 38,
  "Ticket": "PC 17599",
  "Fare": 71.2833,
  "Cabin": "C85"
 },
 
]

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

単純なマッチなら簡単です。containsから指定のキー:値のプロパティが含まれているか(値は部分文字列でよい)の真偽値を得て、それをベースにselectで真のオブジェクトだけを抜き出せばよいのです。

$ cat search-sample.json | jq --arg value Laina '.[] | select(contains({"Name": $value}))'
{
  "id": 3,
  "Name": "Heikkinen, Miss. Laina",
  "Sex": "female",
  "Age": 26,
  "Ticket": "STON/O2. 3101282",
  "Fare": 7.925,
  "Cabin": ""
}

これが、値の大文字小文字に関わらずに検索せよとなると、多少ややこしくなります。私が頂いたオリジナルのお題は「Javaで書け」でしたが、相手はJSONテキストです。jqでできないはずはないじゃないですか(というか、多分、jqの方が楽だと思う)。

コード

コード(フィルタファイル)はsearch.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から呼び出すときは、次のオプションを指定します。

  • --argオプションー検索部分文字列を指定します。ここでは変数名をvalueとしたので、--arg value 文字列となります。
  • -fオプション(--from-file)ーフィルタファイルを読み込みます。

まとめると、次のようになります(検索文字列はlaina)。

$ jq --arg value laina -f search.jq search-sample.json

大文字小文字を区別しないフィルタリング

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

($value | ascii_downcase) as $value |               # 変数 value を小文字化
.[] |                                               # オブジェクト単位のループ
.name = (.Name | ascii_downcase) |                  # 小文字版のname属性を加える
select(contains({"name": $value})) |                # nameをベースにフィルタ
del(.name)                                          # nameを削除

最初の大文字小文字を区別するワンライナーとの違いは、変数valueとプロパティNameの値を、どちらもascii_downcaseで小文字化しているところです(1、3行目)。そして、この小文字化された値を新しいプロパティnameとしてオブジェクトに加えます(3行目)。

これで、containsを用いた内包の真偽判定でnameの方を対象にフィルタリングすれば、大文字小文字を区別しない検索となります。

最後に一時的に用意したnamedelから削除します。

これで上がりです。

実行例を示します。検索文字列が小文字のlainaでもLainaが引っかかることがわかります。

$ cat search-sample.json | jq --arg value lai -f search.jq
{
  "id": 3,
  "Name": "Heikkinen, Miss. Laina",
  "Sex": "female",
  "Age": 26,
  "Ticket": "STON/O2. 3101282",
  "Fare": 7.925,
  "Cabin": ""
}

おわりに

実用性のない、お遊びなjqフィルタを追及していますが、この回は、実用性ありですね。残念。でも、Unixユーザならegrepを使います、きっと。

$ cat search-sample.json | jq -c '.[]' | egrep -i '"Name":".+laina' | jq '.'

ポイントは、-cオプションで複数のプロパティからなるオブジェクトを1行にまとめることで、行志向のgrepでオブジェクト全体を引っかけられるようにしているところです。末尾のjqはこの1行化されたオブジェクトを、読みやすいように1プロパティ1行に戻しています。正規表現は、これだとName以降のプロパティ値にLainaがあったときにも引っかかってしまいます(たとえば "Ticket": "Laina")ので、再考の余地があります。

参考

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?