Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use jq to find all paths to a certain key

Tags:

json

key

extract

jq

In a very large nested json structure I'm trying to find all of the paths that end in a key.

ex:

{
  "A": {
    "A1": {
      "foo": {
        "_": "_"
      }
    },
    "A2": {
      "_": "_"
    }
  },
  "B": {
    "B1": {}
  },
  "foo": {
    "_": "_"
  }
}

would print something along the lines of: ["A","A1","foo"], ["foo"]


Unfortunately I don't know at what level of nesting the keys will appear, so I haven't been able to figure it out with a simple select. I've gotten close with jq '[paths] | .[] | select(contains(["foo"]))', but the output contains all the permutations of any tree that contains foo. output: ["A", "A1", "foo"]["A", "A1", "foo", "_"]["foo"][ "foo", "_"]

Bonus points if I could keep the original data structure format but simply filter out all paths that don't contain the key (in this case the sub trees under "foo" wouldn't need to be hidden).

like image 429
chrisst Avatar asked Sep 30 '16 22:09

chrisst


1 Answers

With your input:

$ jq -c 'paths | select(.[-1] == "foo")' 
["A","A1","foo"]
["foo"]

Bonus points:

(1) If your jq has tostream:

$ jq 'fromstream(tostream| select(.[0]|index("foo")))'

Or better yet, since your input is large, you can use the streaming parser (jq -n --stream) with this filter:

fromstream( inputs|select( (.[0]|index("foo"))))

(2) Whether or not your jq has tostream:

. as $in
| reduce (paths(scalars) | select(index("foo"))) as $p
    (null; setpath($p; $in|getpath($p)))

In all three cases, the output is:

{
  "A": {
    "A1": {
      "foo": {
        "_": "_"
      }
    }
  },
  "foo": {
    "_": "_"
  }
}
like image 146
peak Avatar answered Oct 03 '22 16:10

peak