👌
jqで配列から「in 複数値」的な条件に一致する項目を抽出する
jqで、配列から条件にマッチする項目を取り出すとき、その項目のプロパティが特定の複数値のいずれかに一致すること(複数値に含まれること)という条件を使いたいです。PythonやSQLのin
みたいな。
やりたいこと
例えば、以下のJSON文字列があるとします。
入力
{
"Items": [
{
"Id": "aaa",
"State": "running"
},
{
"Id": "bbb",
"State": "stopping"
},
{
"Id": "ccc",
"State": "stopped"
},
{
"Id": "ddd",
"State": "pending"
}
]
}
このItems
のうち「State
がrunning
またはpending
」の項目を取り出したいです。
select(.State in ["running", "pending"])
みたいな条件は書けないのでしょうか?(※これは動きません)
方法1:or
まず、一番単純な方法が、or
演算子で条件を列挙することです。
コマンド
echo $input | jq '.Items | map(select(.State == "running" or .State == "pending"))'
出力
[
{
"Id": "aaa",
"State": "running"
},
{
"Id": "ddd",
"State": "pending"
}
]
期待する値が2つくらいなら良いですが、それより多くなると何度も.State ==
と書くのが冗長です。
方法2:== ()
意外とこんな書き方で動くようです。
コマンド
echo $input | jq '.Items | map(select(.State == ("running", "pending")))'
マニュアルを見てもなぜこれが機能するのか分かりませんが……
Parenthesis work as a grouping operator just as in any typical programming language.
このコマンドは以下と等価であるそうです[1]。
コマンド
echo $input | jq '.Items | map(select(.State == "running"), select(.State == "pending"))'
この方法は必ずすべての値との比較を実行するので若干効率が悪いという指摘[2]もあります。
方法3:IN()
jq 1.6以降では、SQL-style operatorとしてIN()
がサポートされています。
コマンド
echo $input | jq '.Items | map(select(.State | IN("running", "pending")))'
jq 1.6以降の環境であれば、これが一番良いと思います。
方法4:inside()
これを採用する理由はなさそうですが、一応。
コマンド
echo $input | jq '.Items | map(select([.State] | inside(["running", "pending"])))'
否定するには
ここまでの条件の否定を取る、すなわち、特定の複数値のいずれも含まない項目を抽出するには、以下のようにします。
コマンド
echo $input | jq '.Items | map(select(.State != "running" and .State != "pending"))'
echo $input | jq '.Items | map(select(.State | IN("running", "pending") | not))'
出力
[
{
"Id": "bbb",
"State": "stopping"
},
{
"Id": "ccc",
"State": "stopped"
}
]
Discussion