Wie filtere ich ein Array von Objekten basierend auf Werten in einem inneren Array mit jq?


238

Angesichts dieser Eingabe:

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

Ich versuche, mit jq einen Filter zu erstellen , der alle Objekte mit Ids zurückgibt , die keine "Daten" im inneren NamesArray enthalten, wobei die Ausgabe durch Zeilenumbrüche getrennt ist. Für die obigen Daten möchte ich Folgendes ausgeben

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Ich denke, ich bin etwas nah dran:

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

Der selectFilter ist jedoch nicht korrekt und wird nicht kompiliert (get error: syntax error, unexpected IDENT).

Antworten:


371

Sehr nah! In Ihrem selectAusdruck müssen Sie zuvor eine pipe ( |) verwendencontains .

Dieser Filter erzeugt die erwartete Ausgabe.

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

Das jq-Kochbuch enthält ein Beispiel für die Syntax.

Filtern Sie Objekte basierend auf dem Inhalt eines Schlüssels

ZB möchte ich nur Objekte, deren Genre-Schlüssel "Haus" enthält.

$ 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 beibehalten werden soll, sodass die endgültige Ausgabe ein einzelnes JSON-Array und kein Stream von JSON-Objekten ist.

Am einfachsten ist es, den gesamten Ausdruck in einen Array-Konstruktor zu packen:

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

Sie können auch die Kartenfunktion verwenden:

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

map entpackt das Eingabearray, wendet den Filter auf jedes Element an und erstellt ein neues Array. Mit anderen Worten, map(f)ist äquivalent zu [.[]|f].


Danke, funktioniert super! Ich habe dieses Beispiel tatsächlich gesehen, ich konnte es einfach nicht an mein Szenario anpassen :-)
Abe Voelker

1
Gibt es überhaupt eine Möglichkeit, "die JSON-Struktur des Arrays beizubehalten"? Ich mag das Genre-Beispiel, aber es gibt zwei "json-Zeilen" aus. Ich konnte den Kartenteil nicht unbedingt herausfinden
Colin D

4
@ColinD Ich war mit der Reduktionslösung nicht wirklich zufrieden und habe sie durch eine Erklärung der Kartenfunktion ersetzt. Hilft das?
Iain Samuel McLean Elder

@IainElder - Was passiert, wenn der Teil des Suchbegriffs (in diesem Fall Haus) eine Variable ist? Sagen Sie also mit --args term se. Also enthält ("hou $ term")
SnazzyBootMan

@ Chris Die Variable $termwürde als Zeichenfolge behandelt, daher sollten Sie die Zeichenfolgenverkettung verwenden:contains("hou" + $term)
Iain Samuel McLean Elder

17

Hier ist eine andere Lösung, die any / 2 verwendet

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

mit den Beispieldaten und der -rOption, die es erzeugt

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Genau das, wonach ich gesucht habe - warum funktioniert das mit einem Semikolon .Names[] ; contains()und nicht mit einer Pfeife .Names[] | contains()?
Matt

3
Ah, es ist die any(generator; condition)Form. Ich stellte fest, dass ich ohne Verwendung any()Duplikate in meinen Ergebnissen erhalten würde, wennselect() mehr als einmal mit demselben Objekt übereinstimmte.
Matt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.