Jq ile bir iç dizideki değerlere göre bir nesne dizisine nasıl filtre uygulanır?


240

Bu girdi verildiğinde:

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

Birlikte bir filtre oluşturmak için çalışıyorum JQ bu döner Tüm nesneler Ids olmayan iç olarak "veri" içeren Namesçıkış satır ayrılmış olması ile, bir dizi. Yukarıdaki veriler için, istediğim çıktı

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Sanırım buna biraz yakınım:

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

ancak selectfiltre doğru değil ve derlenmiyor (get error: syntax error, unexpected IDENT).

Yanıtlar:


372

Çok yakın! İfadenizde selectdaha |önce bir pipe ( ) kullanmanız gerekir contains.

Bu filtre beklenen çıktıyı üretir.

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

Jq Yemek sözdizimi bir örnek vardır.

Nesneleri bir anahtarın içeriğine göre filtreleme

Örneğin, sadece tür anahtarı "ev" içeren nesneleri istiyorum.

$ 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 , dizinin JSON yapısının nasıl korunacağını sorar, böylece son çıktı bir JSON nesnesi akışı yerine tek bir JSON dizisidir.

En basit yol tüm ifadeyi bir dizi yapıcısına sarmaktır:

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

Harita işlevini de kullanabilirsiniz:

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

map girdi dizisini açar, filtreyi her öğeye uygular ve yeni bir dizi oluşturur. Başka bir deyişle, map(f)eşdeğerdir [.[]|f].


Teşekkürler, harika çalışıyor! Aslında bu örneği gördüm, senaryoma uyarlamada başarısız oldum :-)
Abe Voelker

1
"Dizinin json yapısını korumak" yine de var mı? Tür örneğini seviyorum ama iki "json satırı" çıktılar. Harita bölümünü mutlaka anlayamadım
Colin D

4
@ColinD İndirgeme çözümünden gerçekten memnun kalmadım, bu yüzden harita işlevinin bir açıklamasıyla değiştirdim. Bu yardımcı olur mu?
Iain Samuel McLean Elder

@IainElder - Arama teriminin (bu durumda evde) bir kısmı değişken olduğunda ne olur? Yani --args terimini se deyin. Yani içerir ("hou $ term")
SnazzyBootMan

@Chris Değişken $termbir dize olarak kabul edilir, bu nedenle dize birleştirme kullanmalısınız:contains("hou" + $term)
Iain Samuel McLean Elder

17

İşte herhangi bir / 2 kullanan başka bir çözüm

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

örnek veriler ve -rürettiği seçenekle

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Tam olarak ne aradım - bu neden .Names[] ; contains()bir boru ile değil noktalı bir kolonla çalışıyor .Names[] | contains()?
Matt

3
Ah, any(generator; condition)form bu. Ben kullanmadan aynı nesne üzerinde birden fazla eşleştirilirse any()sonuçlarımda yinelenen ile sonuçlanacağını buldum select().
Matt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.