web-dev-qa-db-fra.com

Comment additionner les valeurs dans un tableau de cartes en jq?

Étant donné un flux JSON de la forme suivante:

{ "a": 10, "b": 11 } { "a": 20, "b": 21 } { "a": 30, "b": 31 }

Je voudrais additionner les valeurs dans chacun des objets et sortir un seul objet, à savoir:

{ "a": 60, "b": 63 }

Je suppose que cela nécessitera probablement d'aplatir la liste des objets ci-dessus dans un tableau de [name, value] paires puis additionnant les valeurs à l'aide de reduce mais la documentation de la syntaxe d'utilisation de reduce est lamentable.

24
Alan Burlison

A moins que votre jq n'ait inputs, vous devrez Slurper les objets en utilisant le -s drapeau. Ensuite, vous devrez faire pas mal de manipulations:

  1. Chacun des objets doit être mappé en paires clé/valeur
  2. Aplatir les paires en un seul tableau
  3. Regroupez les paires par clé
  4. Mappez chaque groupe en accumulant les valeurs en une seule paire clé/valeur
  5. Mapper les paires à un objet
map(to_entries)
    | add
    | group_by(.key)
    | map({
          key: .[0].key,
          value: map(.value) | add
      })
    | from_entries

Avec jq 1.5, cela pourrait être grandement amélioré: vous pouvez supprimer le slurping et lire directement le inputs directement.

$ jq -n '
reduce (inputs | to_entries[]) as {$key,$value} ({}; .[$key] += $value)
' input.json

Comme nous accumulons simplement toutes les valeurs dans chacun des objets, il sera plus facile de simplement parcourir les paires clé/valeur de toutes les entrées et de les additionner toutes.

23
Jeff Mercado

Une autre approche, qui illustre assez bien la puissance de jq, consiste à utiliser un filtre nommé "somme" défini comme suit:

def sum(f): reduce .[] as $row (0; . + ($row|f) );

Pour résoudre le problème particulier, on pourrait alors utiliser le -s (--Slurp) comme mentionné ci-dessus, avec l'expression:

{"a": sum(.a), "b": sum(.b) }  # (2)

L'expression étiquetée (2) ne calcule que les deux sommes spécifiées, mais elle est facile à généraliser, par ex. comme suit:

# Produce an object with the same keys as the first object in the 
# input array, but with values equal to the sum of the corresponding
# values in all the objects.
def sumByKey:
  . as $in
  | reduce (.[0] | keys)[] as $key
    ( {}; . + {($key): ($in | sum(.[$key]))})
;
6
peak

J'ai rencontré la même question lors de la liste de tous les artefacts de GitHub (voir ici pour plus de détails) et je veux additionner leur taille.

curl https://api.github.com/repos/:owner/:repo/actions/artifacts \
     -H "Accept: application/vnd.github.v3+json" \
     -H "Authorization:  token <your_pat_here>" \
     | jq '.artifacts | map(.size_in_bytes) | add'

Contribution:

{
  "total_count": 3,
  "artifacts": [
    {
      "id": 0000001,
      "node_id": "MDg6QXJ0aWZhY3QyNzUxNjI1",
      "name": "artifact-1",
      "size_in_bytes": 1,
      "url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751625",
      "archive_download_url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751625/Zip",
      "expired": false,
      "created_at": "2020-03-10T18:21:23Z",
      "updated_at": "2020-03-10T18:21:24Z"
    },
    {
      "id": 0000002,
      "node_id": "MDg6QXJ0aWZhY3QyNzUxNjI0",
      "name": "artifact-2",
      "size_in_bytes": 2,
      "url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751624",
      "archive_download_url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2751624/Zip",
      "expired": false,
      "created_at": "2020-03-10T18:21:23Z",
      "updated_at": "2020-03-10T18:21:24Z"
    },
    {
      "id": 0000003,
      "node_id": "MDg6QXJ0aWZhY3QyNzI3NTk1",
      "name": "artifact-3",
      "size_in_bytes": 3,
      "url": "https://api.github.com/repos/docker/mercury-ui/actions/artifacts/2727595",
      "archive_download_url": "https://api.github.com/repos/:owner/:repo/actions/artifacts/2727595/Zip",
      "expired": false,
      "created_at": "2020-03-10T08:46:08Z",
      "updated_at": "2020-03-10T08:46:09Z"
    }
  ]
}

Production:

6
1
Oleg Burov