65

Say I have the following JSON, stored in my variable jsonVariable.

{
    "id": 1,
    "details": {
        "username": "jamesbrown",
        "name": "James Brown"
    }
}

I parse this JSON with jq using the following:

echo $jsonVariable | jq '.details.name | select(.name == "James Brown")'

This would give me the output

James Brown

But what if I want to get the id of this person as well? Now, I'm aware this is a rough and simple example - the program I'm working with at the moment is 5 or 6 levels deep with many different JQ functions other than select. I need a way to select a parent's field when I am already 5 or 6 layers deep after carrying out various methods of filtering.

Can anyone help? Is there any way of 'going in reverse', back up to the parent? (Not sure if I'm making sense!)

stevevls
  • 10,675
  • 1
  • 45
  • 50
x3nr0s
  • 1,946
  • 4
  • 26
  • 46

3 Answers3

103

For a more generic approach, save the value of the "parent" element at the detail level you want, then pipe it at the end of your filter:

jq '. as $parent | .details.name | select(. == "James Brown") | $parent'

Of course, for the trivial case you expose, you could omit this entirely:

jq 'select(.details.name == "James Brown")'

Also, consider that if your selecting filters return many matches for a single parent object, you will receive a copy of the parent object for each match. You may wish to make sure your select filters only return one element at the parent level by wrapping all matches below parent level into an array, or to deduplicate the final result with unique.

  • Great solution. But why are you saying "you will receive a copy of the parent object for each match"? The $parent variable (or more generally we could call it $ancestor) should be bound once, and then as you go down the tree and through any arrays, under that ancestor, you will always get that same identical object that was first bound to the variable, no "copy". And even if it was so, why would anyone care? As long as I can get that handle on the ancestor of my interest, I should be fine, shouldn't I? – Gunther Schadow Feb 09 '21 at 13:17
  • the "copy" part is important if you're modifying the object vs. just reading it. The modifications would occur on the copy, but piping the $parent to the end would just set . to the original, unmodified object. Or put another way, this accepted answer won't work if you want to return the parent after modifying the child. – jamey graham Sep 22 '21 at 18:11
4

Give this a shot:

echo $jsonVariable | jq '{Name: .details.name, Id: .Id}  | select(.name == "James Brown")'
mrtig
  • 2,217
  • 16
  • 26
4

Rather than querying up to the value you're testing for, query up to the root object that contains the value you're querying on and the values you wish to select.

You need the object that contains both the id and the name.

$ jq --arg name 'James Brown' 'select(.details.name == $name).id' input.json
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272