😽

jq commandの select でハマった話

2023/03/26に公開

結論

配列のjsonに対してselectする際には、配列を一度オブジェクトの抽出をしないと複製されてしまう。
なので、以下ではなく

jq -r  'select(.[].A | contains("特定文字列")) | .[].B' test.json

こうしないといけない

jq -r  '.[] | select(.A | contains("特定文字列")) | .B' test.json

環境

$ jq --version   
jq-1.6

詰まった内容

以下のjson(test.json)があったときに

test.json
[
    {
        "hoge": "aba",
        "fuga": "bbb"
    },
    {
        "hoge": "ccc",
        "fuga": "ddd"
    },
    {
        "hoge": "aab",
        "fuga": "ccc"
    }
]

hogeのvalueで ab が入っているfugaの値を抜き出したいとします。以下の形を想定しています。

bbb
ccc

この際に以下のような書き方をすると

jq -r  'select(.[].hoge | contains("ab")) | .[].fuga' test.json

結果は以下のようになります。

aba
ccc
aab
aba
ccc
aab

検証

試しに、以下のようにやってみるとリストが3つ作成されています。

jq -r  'select(.[].hoge)' test.json
結果
[
  {
    "hoge": "aba",
    "fuga": "bbb"
  },
  {
    "hoge": "ccc",
    "fuga": "ddd"
  },
  {
    "hoge": "aab",
    "fuga": "ccc"
  }
]
[
  {
    "hoge": "aba",
    "fuga": "bbb"
  },
  {
    "hoge": "ccc",
    "fuga": "ddd"
  },
  {
    "hoge": "aab",
    "fuga": "ccc"
  }
]
[
  {
    "hoge": "aba",
    "fuga": "bbb"
  },
  {
    "hoge": "ccc",
    "fuga": "ddd"
  },
  {
    "hoge": "aab",
    "fuga": "ccc"
  }
]

で以下のように | contains("ab") をつけると2つになっています

jq -r  'select(.[].hoge | contains("ab"))' test.json
結果
[
  {
    "hoge": "aba",
    "fuga": "bbb"
  },
  {
    "hoge": "ccc",
    "fuga": "ddd"
  },
  {
    "hoge": "aab",
    "fuga": "ccc"
  }
]
[
  {
    "hoge": "aba",
    "fuga": "bbb"
  },
  {
    "hoge": "ccc",
    "fuga": "ddd"
  },
  {
    "hoge": "aab",
    "fuga": "ccc"
  }
]

つまり、先程の場合、「リスト一つを一つのオブジェクトとして認識しつつ、かつ、その中の .hoge の値を検証するため、各リストの中の .hoge の分だけオブジェクト(リスト)が作られ、それぞれを検証する」という感じのようです。

なので、今回の要件に合わせるには、一度リストからオブジェクトを取得してから実施する .[] を最初にやる必要があるということですね。

jq -r '.[] | select(.hoge | contains("ab"))' test.json 
結果
{
  "hoge": "aba",
  "fuga": "bbb"
}
{
  "hoge": "aab",
  "fuga": "ccc"
}

だとしても、増えるのは。。。良くないよなぁ、と勝手に思っていた土曜日の夜。

Discussion