web-dev-qa-db-fra.com

Comment filtrer tableau d'objets par valeurs de propriétés d'élément en utilisant jq?

J'aime filtrer les fichiers json avec jq :

jq . some.json

Étant donné le json contenant un tableau d'objets:

{
  "theList": [
    {
      "id": 1,
      "name": "Horst"
    },
    {
      "id": 2,
      "name": "Fritz"
    },
    {
      "id": 3,
      "name": "Walter"
    },
    {
      "id": 4,
      "name": "Gerhart"
    },
    {
      "id": 5,
      "name": "Harmut"
    }
  ]
}

Je souhaite filtrer cette liste pour ne montrer que les éléments dont l'id a la valeur 2 et 4, le résultat attendu est donc:

{
  "id": 2,
  "name": "Fritz"
},
{
  "id": 4,
  "name": "Gerhart"
}

Comment filtrer le json avec jq? J'ai joué avec select and map, mais aucun de ceux-ci n'a fonctionné, par exemple:

$ jq '.theList[] | select(.id == 2) or select(.id == 4)' array.json
true
17
k0pernikus

À partir de la documentation:

jq '.[] | select(.id == "second")' 

Entrée [{"id": "first", "val": 1}, {"id": "second", "val": 2}] 

Sortie {"id": "second", "val": 2}

Je pense que vous pouvez faire quelque chose comme ça:

jq '.theList[] | select(.id == 2 or .id == 4)' array.json
27
André Senra

Vous pouvez utiliser select dans map.

.theList | map(select(.id == (2, 4)))

Ou plus compact:

[ .theList[] | select(.id == (2, 4)) ]

Bien qu'écrit de cette façon est un peu inefficace car l'expression est dupliquée pour chaque valeur comparée. Ce sera plus efficace et peut-être plus lisible, écrit de cette façon:

[ .theList[] | select(any(2, 4; . == .id)) ]
7
Jeff Mercado

Utiliser select(.id == (2, 4)) ici est généralement inefficace (voir ci-dessous).

Si votre jq a IN/1, alors il peut être utilisé pour obtenir une solution plus efficace:

.theList[] | select( .id | IN(2,3))

Si votre jq n'a pas IN/1, vous pouvez le définir comme suit: 

def IN(s): first(select(s == .)) // false;

Efficacité

Une façon de voir l'inefficacité consiste à utiliser debug. L'expression suivante, par exemple, entraîne 10 appels à debug, alors que seulement 9 contrôles d'égalité sont réellement nécessaires:

.theList[] | select( (.id == (2,3)) | debug )

["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",true]
{
  "id": 2,
  "name": "Fritz"
}
["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",true]
{
  "id": 3,
  "name": "Walter"
}
["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",false]
["DEBUG:",false]

index/1

En principe, utiliser index/1 devrait être efficace, mais à ce jour (octobre 2017), sa mise en œuvre, bien que rapide (elle est écrite en C), est inefficace.

2
peak

Voici une solution utilisant indices :

.theList | [ .[map(.id)|indices(2,4)[]] ]
0
jq170727