skip to Main Content

I would like to produce a list of all unique key path from a given JSON, each one in a line.

For example, from this input JSON:

{
   "results":[
      {
         "id":306,
         "name":"First Company",
         "branches":[
            {
               "id":4191,
               "city":"San Francisco",
               "customers":[
                  {
                     "id":446,
                     "name":"Big Tech 1"
                  },
                  {
                     "id":447,
                     "name":"Big Tech 2"
                  }
               ]
            },
            {
               "id":4192,
               "city":"Miami",
               "customers":[
                  {
                     "id":448,
                     "name":"Insurance Tech 1"
                  },
                  {
                     "id":449,
                     "name":"Health Tech 2"
                  }
               ]
            }
         ]
      }
   ]
}

I would like to produce this output:

  results
  results.id
  results.name
  results.branches
  results.branches.id
  results.branches.city
  results.branches.customers
  results.branches.customers.id
  results.branches.customers.name

I am struggling with the command line below but it is not working.

jq '
def path(k):
  if k | type == "object":
    reduce(.keys[] | path(.), k)
  else:
    [k]
;

.[] | path(".") | flatten | sort' example.json

Any help, please?

3

Answers


  1. Here is how I would do it:

    jq -r '
      def allpaths:
        paths
        | map(if type == "number" then "*" else . end)
        | join(".") ;
    
      [allpaths] | unique[]
    

    With the sample JSON, this produces:

    results
    results.*
    results.*.branches
    results.*.branches.*
    results.*.branches.*.city
    results.*.branches.*.customers
    results.*.branches.*.customers.*
    results.*.branches.*.customers.*.id
    results.*.branches.*.customers.*.name
    results.*.branches.*.id
    results.*.id
    results.*.name
    
    Login or Signup to reply.
  2. With paths function, filtering out array indices of object keys and collecting unique paths:

    jq '[paths(.) | map(select(type != "number")) | join(".")] | unique[]' test.json 
    

    "results"
    "results.branches"
    "results.branches.city"
    "results.branches.customers"
    "results.branches.customers.id"
    "results.branches.customers.name"
    "results.branches.id"
    "results.id"
    "results.name"
    
    Login or Signup to reply.
  3. You could map the paths to only contain strings, then drop the duplicates using unique, and join the items by providing a delimiter:

    jq -r '[paths | map(strings)] | unique[] | join(".")'
    
    results
    results.branches
    results.branches.city
    results.branches.customers
    results.branches.customers.id
    results.branches.customers.name
    results.branches.id
    results.id
    results.name
    

    Demo

    To retain the document order (unique also sorts the array), you could deduplicate the list by using the joined paths as (unique-by-definition) object fields instead:

    jq -r '[paths | map(strings) | join(".") | {(.):.}] | add[]'
    
    results
    results.id
    results.name
    results.branches
    results.branches.id
    results.branches.city
    results.branches.customers
    results.branches.customers.id
    results.branches.customers.name
    

    Demo

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search