jqで内部配列の値に基づいてオブジェクトの配列をフィルタリングする方法


238

この入力が与えられた場合:

[
  {
    "Id": "cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b",
    "Names": [
      "condescending_jones",
      "loving_hoover"
    ]
  },
  {
    "Id": "186db739b7509eb0114a09e14bcd16bf637019860d23c4fc20e98cbe068b55aa",
    "Names": [
      "foo_data"
    ]
  },
  {
    "Id": "a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19",
    "Names": [
      "jovial_wozniak"
    ]
  },
  {
    "Id": "76b71c496556912012c20dc3cbd37a54a1f05bffad3d5e92466900a003fbb623",
    "Names": [
      "bar_data"
    ]
  }
]

内部配列に「データ」を含まない sのすべてのオブジェクトを返し、出力が改行で区切られるjqを使用してフィルターを構築しようとしています。上記のデータについて、私が望む出力はIdNames

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

私はこれに多少近づいていると思います:

(. - select(.Names[] contains("data"))) | .[] .Id

しかし、selectフィルターは正しくなく、コンパイル(get error: syntax error, unexpected IDENT)されません。

回答:


371

とても近い!あなたにselect表現、あなたはパイプを(使用する必要があります|前に)contains

このフィルターは、予期される出力を生成します。

. - map(select(.Names[] | contains ("data"))) | .[] .Id

JQクックブックは、構文の例があります。

キーの内容に基づいてオブジェクトをフィルタリングする

たとえば、ジャンルキーに「家」が含まれているオブジェクトのみが必要です。

$ json='[{"genre":"deep house"}, {"genre": "progressive house"}, {"genre": "dubstep"}]'
$ echo "$json" | jq -c '.[] | select(.genre | contains("house"))'
{"genre":"deep house"}
{"genre":"progressive house"}

Colin Dは、配列のJSON構造を保存する方法を尋ね、最終的な出力がJSONオブジェクトのストリームではなく単一のJSON配列になるようにします。

最も簡単な方法は、式全体を配列コンストラクターでラップすることです。

$ echo "$json" | jq -c '[ .[] | select( .genre | contains("house")) ]'
[{"genre":"deep house"},{"genre":"progressive house"}]

map関数を使用することもできます。

$ echo "$json" | jq -c 'map(select(.genre | contains("house")))'
[{"genre":"deep house"},{"genre":"progressive house"}]

mapは入力配列をアンパックし、すべての要素にフィルターを適用して、新しい配列を作成します。つまり、map(f)と同等[.[]|f]です。


ありがとうございます。私は実際にその例を見ました、それを私のシナリオに適応するのに失敗しました:-)
Abe Voelker

1
とにかく「配列のjson構造を保持」する方法はありますか?ジャンルの例は好きですが、2つの「json行」が出力されます。私は必ずしもマップパーツを理解することができませんでした
コリンD

4
@ColinDリデュースソリューションに満足できなかったので、マップ関数の説明に置き換えました。それは役に立ちますか?
Iain Samuel McLean Elder

@IainElder-検索語の一部(この場合はhouse)が変数である場合はどうなりますか?したがって、-args term seを使用するとします。つまり、contains( "hou $ term")
SnazzyBootMan 2017年

変数は@クリス$termあなたは文字列の連結を使用する必要がありますので、文字列として扱われます:contains("hou" + $term)
イアンサミュエル・マクリーンエルダー

17

any / 2を使用する別のソリューションを次に示します

map(select(any(.Names[]; contains("data"))|not)|.Id)[]

サンプルデータと-rそれが生成するオプション

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

まさに私が探していたもの-なぜこれ.Names[] ; contains()はパイプではなくセミコロンで機能するの.Names[] | contains()ですか?
マット

3
ああ、それはany(generator; condition)形です。同じオブジェクトで2回以上一致したany()場合、使用しないと結果が重複してしまうことがわかりましたselect()
Matt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.