¿Cómo filtrar una matriz de objetos basada en valores en una matriz interna con jq?


Dada esta entrada:

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

Estoy tratando de construir un filtro con jq que devuelve todos los objetos con Id s que no contienen "datos" en el array interno Names, con la salida separada por una nueva línea. Para los datos anteriores, la salida que me gustaría es

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19

Creo que estoy un poco cerca de esto:

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

Pero el filtro select no es correcto y no compila (get error: syntax error, unexpected IDENT).

Author: peak, 2014-11-02

2 answers

Muy cerca! En su expresión select, debe usar una tubería (|) antes de contains.

Este filtro produce la salida esperada.

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

El jq Cookbook tiene un ejemplo de la sintaxis.

Filtrar objetos basados en el contenido de una clave

Por ejemplo, solo quiero objetos cuya clave de género 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 pregunta cómo preservar la estructura JSON de la matriz, de modo que la salida final sea una sola matriz JSON en lugar de una secuencia de objetos JSON.

La forma más sencilla es envolver toda la expresión en un constructor de matriz:

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

También puede utilizar la función de mapa:

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

Map desempaqueta la matriz de entrada, aplica el filtro a cada elemento y crea una nueva matriz. En otras palabras, map(f) es equivalente a [.[]|f].

 218
Author: Iain Samuel McLean Elder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:56

Aquí hay otra solución que utiliza cualquier/2

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

Con los datos de muestra y la opción -r produce

cb94e7a42732b598ad18a8f27454a886c1aa8bbba6167646d8f064cd86191e2b
a4b7e6f5752d8dcb906a5901f7ab82e403b9dff4eaaeebea767a04bac4aada19
 8
Author: jq170727,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-31 00:53:24