jq를 사용하여 내부 배열의 값을 기반으로 객체 배열을 필터링하는 방법은 무엇입니까?


239

이 입력이 주어지면 :

[
  {
    "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).

답변:


372

아주 근접한! 당신에 select표현, 당신은 파이프 (사용해야 |하기 전에) contains.

이 필터는 예상 출력을 생성합니다.

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

JQ 해설서는 구문의 일례가있다.

키의 내용을 기준으로 객체 필터링

예를 들어, 장르 키에 "house"가 포함 된 개체 만 원합니다.

$ 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은 입력 배열의 압축을 풀고 모든 요소에 필터를 적용하고 새 배열을 만듭니다. 즉, map(f)와 같습니다 [.[]|f].


고마워요, 잘 작동합니다! 나는 실제로 그 예를 보았습니다. 방금 시나리오에 적응하지 못했습니다 :-)
Abe Voelker

1
어쨌든 "배열의 json 구조를 유지"해야합니까? 장르 예제를 좋아하지만 두 개의 "json line"을 출력합니다. 지도 부분을 반드시 파악할 수 없었습니다
Colin D

4
@ ColinD 나는 감소 솔루션에 정말로 만족하지 않았으므로 맵 기능에 대한 설명으로 대체했습니다. 도움이 되나요?
Iain Samuel McLean Elder

@IainElder-검색어 (이 경우 하우스)의 일부가 변수 인 경우 어떻게됩니까? --args term se를 사용하십시오. 따라서 contains ( "hou $ term")
SnazzyBootMan

@Chris 변수 $term는 문자열로 취급되므로 문자열 연결을 사용해야합니다.contains("hou" + $term)
Iain Samuel McLean Elder

17

다음은 any / 2 를 사용 하는 다른 솔루션입니다.

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

샘플 데이터 및 -r생성 옵션

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

정확히 내가 찾던 것-왜 .Names[] ; contains()파이프가 아닌 세미콜론으로 작동 .Names[] | contains()합니까?
Matt

3
아, any(generator; condition)형식입니다. 동일한 객체에서 두 번 이상 일치 any()하면 사용하지 않으면 결과에 중복이 발생 select()합니다.
Matt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.