Come filtrare una matrice di oggetti in base ai valori in una matrice interna con jq?


240

Dato questo input:

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

Sto cercando di costruire un filtro con jq che restituisca tutti gli oggetti con Ids che non contengano "dati" Namesnell'array interno , con l'output separato da newline. Per i dati di cui sopra, l'output che mi piacerebbe è

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Penso di essere un po 'vicino a questo:

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

ma il selectfiltro non è corretto e non viene compilato (get error: syntax error, unexpected IDENT).

Risposte:


372

Molto vicino! Nella tua selectespressione, devi usare una pipe ( |) prima contains.

Questo filtro produce l'output previsto.

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

Il libro di cucina jq ha un esempio della sintassi.

Filtra gli oggetti in base al contenuto di una chiave

Ad esempio, voglio solo oggetti la cui chiave di genere contenga "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 chiede come preservare la struttura JSON dell'array, in modo che l'output finale sia un singolo array JSON anziché un flusso di oggetti JSON.

Il modo più semplice è avvolgere l'intera espressione in un costruttore di array:

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

Puoi anche usare la funzione mappa:

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

map disimballa l'array di input, applica il filtro a ogni elemento e crea un nuovo array. In altre parole, map(f)equivale a [.[]|f].


Grazie, funziona benissimo! In realtà ho visto quell'esempio, non sono riuscito ad adattarlo al mio scenario :-)
Abe Voelker,

1
Esiste un modo per "preservare la struttura json dell'array"? Mi piace l'esempio di genere ma produce due "linee json". Non riuscivo a capire necessariamente la parte della mappa
Colin D,

4
@ColinD Non ero davvero soddisfatto della soluzione di riduzione, quindi l'ho sostituita con una spiegazione della funzione della mappa. Questo aiuta?
Iain Samuel McLean Elder il

@IainElder - Cosa succede quando la parte del termine di ricerca (in questo caso house) è una variabile? Quindi dire usando --args termine se. Quindi contiene ("hou $ term")
SnazzyBootMan

@Chris La variabile $termverrebbe trattata come una stringa, quindi dovresti usare la concatenazione delle stringhe:contains("hou" + $term)
Iain Samuel McLean Elder

17

Ecco un'altra soluzione che utilizza any / 2

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

con i dati di esempio e l' -ropzione che produce

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Esattamente quello che stavo cercando: perché funziona con un punto e virgola .Names[] ; contains()e non con una pipa .Names[] | contains()?
Matt,

3
Ah, è la any(generator; condition)forma. Ho scoperto che senza usare any()finirei con i duplicati nei miei risultati se select()abbinato più di una volta sullo stesso oggetto.
Matt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.