'jq - reduce array of paths using subpaths

Assume the following JSON data:

{
  "results": [
    {
      "path": "other/foo/1.15.1",
      "modified": "2022-02-09T13:45:34.638Z"
    },
    {
      "path": "other/foo/1.15.0",
      "modified": "2022-02-09T10:00:40.681Z"
    },
    {
      "path": "a/b/foo/1.0.3-SNAPSHOT",
      "modified": "2022-03-04T19:07:30.262Z"
    },
    {
      "path": "a/b/c/0.1.1",
      "modified": "2022-01-10T15:12:00.597Z"
    },
    {
      "path": "a/b/c/0.1.0",
      "modified": "2022-01-07T18:05:23.158Z"
    },
    {
      "path": "a/b/c/0.0.9",
      "modified": "2021-12-06T10:29:31.161Z"
    }
  ]
}

And also assume, that it is descendingly sorted first per path then per modified.

Now, for every path without its last part, I would like to find only the latest ones according to the modified field (which are always on the top because of the sorting). Then, output that path including its last part again.

In other words, for this example data the output should be:

other/foo/1.15.1
a/b/foo/1.0.3-SNAPSHOT
a/b/c/0.1.1
jq


Solution 1:[1]

Extract the common path to a new field, group the objects by the new field, and select the one with the maximal modified field.

jq -r '[.results[]
          | .short = .path[0:(.path | rindex("/"))]
       ]
       | group_by(.short)[]
       | max_by(.modified).path
      ' file.json

Solution 2:[2]

I came up with a solution using foreach and regular expressions, based on this answer https://stackoverflow.com/a/29829035/1851290

jq -n -r 'foreach (inputs | .results| .[]) as $k (
             {};

             .[$k.path | sub("/[^/]*$"; "")]+=1;

             if .[$k.path | sub("/[^/]*$"; "")]==1 then
                 $k.path
             else
                 empty
             end
         )'

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 choroba
Solution 2 Danny Lo