lukamus
lukamus

Reputation: 13

Filter JSON outer objects including only one maximal object from inner array with jq

I have a slightly modified JSON file with iOS firmware information from https://api.ipsw.me/v2.1/firmwares.json. This is a simplified version:

Input

{
  "AppleTV5,3": {
    "firmwares": [
      {
        "version": "9.2",
        "buildid": "13Y234"
      },
      {
        "version": "9.1.1",
        "buildid": "13U717"
      },
      {
        "version": "9.1",
        "buildid": "13U85"
      }
    ],
    "bdid": 52,
    "name": "Apple TV 4 (2015)"
  },
  "AppleTV3,2": {
    "firmwares": [
      {
        "version": "8.4.1",
        "buildid": "12H523"
      },
      {
        "version": "8.3",
        "buildid": "12F69"
      }
    ],
    "bdid": 0,
    "name": "Apple TV 3 (2013)"
  },
  "AppleTV3,1": {
    "firmwares": [
      {
        "version": "8.4.1",
        "buildid": "12H523"
      },
      {
        "version": "8.3",
        "buildid": "12F69"
      },
      {
        "version": "8.2",
        "buildid": "12D508"
      }
    ],
    "bdid": 0,
    "name": "Apple TV 3"
  }
}

I want to write a jq query that returns each outer object with only the latest firmware object in the firmwares array. E.g.:

Desired output

{
  "AppleTV5,3": {
    "firmwares": [
      {
        "version": "9.2",
        "buildid": "13Y234"
      }
    ],
    "bdid": 52,
    "name": "Apple TV 4 (2015)"
  },
  "AppleTV3,2": {
    "firmwares": [
      {
        "version": "8.4.1",
        "buildid": "12H523"
      }
    ],
    "bdid": 0,
    "name": "Apple TV 3 (2013)"
  },
  "AppleTV3,1": {
    "firmwares": [
      {
        "version": "8.4.1",
        "buildid": "12H523"
      }
    ],
    "bdid": 0,
    "name": "Apple TV 3"
  }
}

I can get a list of the latest firmwares objects with:

.[].firmwares | max_by(.version)

I can get just the values from version with:

.[].firmwares | map(.version | values) | max

And I can get the outer AppleTV objects with firmwares matching a particular version:

[ . | to_entries[] | .value.firmwares |= map ( select ( .version == "8.3" ) ) ] | from_entries

But I can't seem to combine the techniques to get my desired output. Can anyone help out?

Upvotes: 1

Views: 231

Answers (2)

jq170727
jq170727

Reputation: 14663

Here is a solution which uses reduce to visit each key of the object and update corresponding .firmwares to the latest version

reduce keys[] as $i (
    .
  ; .[$i].firmwares |= [max_by(.version)]
)

Upvotes: 1

Jeff Mercado
Jeff Mercado

Reputation: 134881

You should think of it as updating the firmwares arrays. We're updating it with a filtered version where it the max version is selected.

.[].firmwares |= [ max_by(.version | split(".") | map(tonumber)) // empty ]

Upvotes: 1

Related Questions