2014-11-02 8 views
101

diesen Eingang Gegeben:Wie filtert man ein Array von Objekten basierend auf Werten in einem inneren Array mit jq?

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

Ich versuche, einen Filter mit jq zu konstruieren, dass alle Objekte mit Id s liefert, dass nicht „Daten“ in der inneren Names Array enthalten, mit dem sein Newline Ausgang -getrennt. Für die oben genannten Daten ist der Ausgang Ich mag würde

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b 
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19 

Ich glaube, ich mit diesem etwas in der Nähe bin:

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

aber die select Filter ist nicht richtig, und es lässt sich nicht kompilieren (get error: syntax error, unexpected IDENT).

Antwort

171

Sehr nah! In Ihrem select Ausdruck müssen Sie ein Rohr (|) vor contains verwenden.

Dieser Filter erzeugt die erwartete Ausgabe.

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

Die jq Cookbook hat ein Beispiel der Syntax.

Filter-Objekte basierend auf dem Inhalt eines Schlüssels

Z. B., ich möchte nur Objekte, deren Genre Schlüssel enthält „Haus“.

$ 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 fragt, wie die JSON Struktur des Arrays zu erhalten, so dass die endgültige Ausgabe eine einzige JSON Array anstatt ein Strom von JSON Objekten.

Der einfachste Weg ist es, den gesamten Ausdruck in einem Array Konstruktor zu wickeln:

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

Sie können auch die Kartenfunktion nutzen:

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

Karte entpackt den Eingangs-Array, wendet den Filter auf jedes Element und erstellt ein neues Array. Mit anderen Worten, map(f) entspricht [.[]|f].

+0

Danke, funktioniert super! Ich habe dieses Beispiel tatsächlich gesehen, es ist mir einfach nicht gelungen, es an mein Szenario anzupassen :-) –

+0

Gibt es trotzdem "die JSON-Struktur des Arrays zu erhalten"? Ich mag das Genre Beispiel, aber es gibt zwei "JSON-Linien" aus. Ich konnte den Map-Teil nicht unbedingt –

+0

@ColinD herausfinden, überprüfe mein Update für zwei Lösungen. –

3

ist hier eine andere Lösung, die any/2

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

mit den Beispieldaten verwendet und die -r Option es produziert

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b 
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19 
Verwandte Themen