2014-11-02 13 views
101

Dato questo ingresso:Come filtrare un array di oggetti in base ai valori in un array interno con jq?

[ 
    { 
    "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 restituisce tutti gli oggetti con Id s che fanno non contengono "dati" nel interno Names matrice, con l'uscita essendo newline -separato. Per i dati di cui sopra, l'uscita mi piacerebbe è

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b 
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19 

Credo di essere un po 'vicino a questo:

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

ma il filtro select non è corretto e non viene compilato (ottenere error: syntax error, unexpected IDENT).

risposta

171

Molto vicino! Nell'espressione select, è necessario utilizzare una pipe (|) prima dello contains.

Questo filtro produce l'uscita prevista.

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

Il jq Cookbook ha un esempio della sintassi.

oggetti Filter in base al contenuto di una chiave

esempio voglio solo oggetti la cui chiave di genere contiene "casa".

$ 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 il risultato finale è un singolo array JSON piuttosto che un flusso di oggetti JSON.

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

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

È inoltre possibile utilizzare la funzione di mappa:

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

mappa spacchetta la matrice di ingresso, si applica il filtro ogni elemento e crea una nuova matrice. In altre parole, map(f) equivale a [.[]|f].

+0

Grazie, funziona benissimo! Ho effettivamente visto quell'esempio, ho appena fallito ad adattarlo al mio scenario :-) –

+0

Esiste comunque "preservare la struttura JSON dell'array"? Mi piace l'esempio di genere ma emette due "linee json". Non riuscivo a capire la parte della mappa necessariamente –

+0

@ColinD, controlla il mio aggiornamento per due soluzioni. –

3

Ecco un'altra soluzione che utilizza any/2

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

con i dati di esempio e l'opzione -r Produce

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b 
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19 
Problemi correlati