Làm cách nào để lọc một mảng các đối tượng dựa trên các giá trị trong một mảng bên trong với jq?


238

Cho đầu vào này:

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

Tôi đang cố gắng xây dựng bộ lọc với jq trả về tất cả các đối tượng có Ids không chứa "dữ liệu" trong Namesmảng bên trong , với đầu ra được phân tách bằng dòng mới. Đối với dữ liệu trên, đầu ra tôi muốn là

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Tôi nghĩ rằng tôi hơi gần với điều này:

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

nhưng selectbộ lọc không chính xác và nó không biên dịch (get error: syntax error, unexpected IDENT).

Câu trả lời:


371

Rất gần! Trong selectbiểu thức của bạn , bạn phải sử dụng một đường ống ( |) trước contains.

Bộ lọc này tạo ra đầu ra dự kiến.

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

Các Cookbook JQ có một ví dụ về cú pháp.

Lọc các đối tượng dựa trên nội dung của khóa

Ví dụ, tôi chỉ muốn các đối tượng có khóa thể loại chứa "nhà".

$ 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 hỏi cách bảo toàn cấu trúc JSON của mảng, sao cho đầu ra cuối cùng là một mảng JSON chứ không phải là một luồng các đối tượng JSON.

Cách đơn giản nhất là bọc toàn bộ biểu thức trong một hàm tạo mảng:

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

Bạn cũng có thể sử dụng chức năng bản đồ:

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

ánh xạ giải nén mảng đầu vào, áp dụng bộ lọc cho mọi phần tử và tạo một mảng mới. Nói cách khác, map(f)tương đương với [.[]|f].


Cảm ơn, làm việc tuyệt vời! Tôi thực sự đã thấy ví dụ đó, tôi đã thất bại trong việc điều chỉnh nó theo kịch bản của mình :-)
Abe Voelker

1
Có cách nào để "bảo toàn cấu trúc json của mảng" không? Tôi thích ví dụ thể loại nhưng nó cho ra hai "dòng json". Tôi không thể tìm ra phần bản đồ nhất thiết phải có
Colin D

4
@ColinD Tôi không thực sự hài lòng với giải pháp rút gọn, vì vậy tôi đã thay thế nó bằng một lời giải thích về chức năng bản đồ. cái đó có giúp ích không?
Iain Samuel McLean Elder

@IainElder - Điều gì xảy ra khi một phần của thuật ngữ tìm kiếm (trong trường hợp này) là một biến? Vì vậy, nói rằng sử dụng --args hạn se. Vì vậy, có chứa ("hou $ term")
SnazzyBootMan

@Chris Biến $termsẽ được coi là một chuỗi, vì vậy bạn nên sử dụng nối chuỗi:contains("hou" + $term)
Iain Samuel McLean Elder

17

Đây là một giải pháp khác sử dụng bất kỳ / 2

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

với dữ liệu mẫu và -rtùy chọn mà nó tạo ra

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Chính xác những gì tôi đang tìm kiếm - tại sao điều này hoạt động với một dấu chấm phẩy .Names[] ; contains()mà không phải với một đường ống .Names[] | contains()?
Matt

3
Ah, đó là any(generator; condition)hình thức. Tôi thấy rằng không sử dụng any()tôi sẽ kết thúc với các bản sao trong kết quả của mình nếu select()khớp nhiều lần trên cùng một đối tượng.
Matt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.