วิธีการกรองอาร์เรย์ของวัตถุตามค่าในอาร์เรย์ภายในด้วย jq


239

รับข้อมูลนี้:

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

ฉันกำลังพยายามสร้างตัวกรองด้วยjqที่ส่งคืนวัตถุทั้งหมดด้วยIds ที่ไม่มี "data" ในNamesอาร์เรย์ภายในโดยที่เอาต์พุตถูกคั่นด้วยบรรทัดใหม่ สำหรับข้อมูลข้างต้นผลลัพธ์ที่ฉันต้องการคือ

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

ฉันคิดว่าฉันค่อนข้างใกล้กับสิ่งนี้:

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

แต่selectตัวกรองไม่ถูกต้องและไม่ได้รวบรวม (รับerror: syntax error, unexpected IDENT)

คำตอบ:


372

ใกล้มาก! ของคุณใน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"}]

นอกจากนี้คุณยังสามารถใช้ฟังก์ชั่นแผนที่:

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

แผนที่คลายอาร์เรย์อินพุตนำตัวกรองไปใช้กับทุกองค์ประกอบและสร้างอาร์เรย์ใหม่ ในคำอื่น ๆเทียบเท่ากับmap(f)[.[]|f]


ขอบคุณใช้งานได้ดี! จริง ๆ แล้วฉันเห็นตัวอย่างที่ฉันเพิ่งล้มเหลวในการปรับให้เข้ากับสถานการณ์ของฉัน :-)
Abe Voelker

1
มีอยู่เพื่อ "รักษาโครงสร้าง json ของอาร์เรย์" หรือไม่ ฉันชอบตัวอย่างประเภท แต่มันจะแสดงผลสองบรรทัด "json บรรทัด" ฉันไม่สามารถเข้าใจส่วนแผนที่ได้อย่างแน่นอน
โคลิน D

4
@ColinD ฉันไม่พอใจกับวิธีลดขนาดดังนั้นฉันจึงแทนที่ด้วยคำอธิบายฟังก์ชั่นแผนที่ มันช่วยได้ไหม
เลนซามูเอลแมคลีนผู้อาวุโส

@IainElder - จะเกิดอะไรขึ้นเมื่อส่วนของคำค้นหา (ในกรณีที่บ้านนี้) เป็นตัวแปร? ดังนั้นพูดโดยใช้ - คำศัพท์ se ดังนั้นมี ("hou $ term")
SnazzyBootMan

@Chris ตัวแปร$termจะถือว่าเป็นสตริงดังนั้นคุณควรใช้การเรียงสตริง:contains("hou" + $term)
Iain Samuel McLean Elder

17

นี่คือวิธีแก้ปัญหาอื่นที่ใช้ใด ๆ / 2

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

ด้วยข้อมูลตัวอย่างและ-rตัวเลือกที่สร้างขึ้น

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

สิ่งที่ฉันกำลังมองหา - ทำไมจึงทำงานกับลำไส้ใหญ่.Names[] ; contains()และไม่ได้ไปกับท่อ.Names[] | contains()?
แมตต์

3
อามันเป็นany(generator; condition)รูปแบบ ฉันพบว่าไม่มีการใช้any()ฉันจะจบลงด้วยการซ้ำซ้อนในผลลัพธ์ของฉันหากselect()จับคู่มากกว่าหนึ่งครั้งในวัตถุเดียวกัน
Matt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.