jq - Return all top-level attribute keys which have a subattribute that matches a condition

时间:2018-08-22 13:40:53

标签: jq

My JSON looks like:

{
  "foo": {
    "restricted": true
  },
  "bar": {
    "restricted": false
  },
  "baz": { }
}

As single line:

{"foo":{"restricted":true},"bar":{"restricted":false},"baz":{}}

I want to get all top-level attribute keys returned which are either restricted == false or don't have the restricted subattribute. With above example JSON, the expected output is:

"bar"
"baz"

1 个答案:

答案 0 :(得分:2)

I came up with the following expression:

jq "to_entries[] | select(.value.restricted|not) | .key"

to_entries converts the top-level object to an array of objects with a "key" and a "value" attribute each:

[
  {
    "key": "foo135",
    "value": {
      "restricted": true
    }
  },
  {
    "key": "foo246",
    "value": {
      "restricted": false
    }
  },
  {
    "key": "foo345",
    "value": {}
  }
]

[] explodes this array. This is done in order to filter individual objects in the next step:

{
  "key": "foo135",
  "value": {
    "restricted": true
  }
}
{
  "key": "foo246",
  "value": {
    "restricted": false
  }
}
{
  "key": "foo345",
  "value": {}
}

Then a filter is applied for each object: select(.value.restricted|not). To filter out attributes with the subattribute restricted being truthy, .value.restricted can be inverted with |not to select objects with restricted == false as well as objects without this attribute using a single condition:

{
  "key": "foo246",
  "value": {
    "restricted": false
  }
}
{
  "key": "foo345",
  "value": {}
}

Finally, the "key" attribute is selected: .key. This returns the names of the matching objects (originally the top-level attribute keys):

"foo246"
"foo345"

Note that the pipe character | in select(...) | .key isn't strictly needed. The result is the same without.

If a string array is desired as result...

[
  "foo246",
  "foo345"
]

... then the following expression can be used:

jq "to_entries | map(select(.value.restricted|not).key)"

As you can see, select() can be used inside of map() to filter out individual elements, and it can even be combined with the attribute filter .key.

If you append [] at the end like map(...)[], then the array gets exploded and the result is a list of strings again, like in the initial example, but using a slightly different approach.