3

I'm using jq 1.4. I trying to select elements when VPCZoneIdentifier exists in my $selected_subnets (bash variable).

selected_subnets="valueA valueB"

input='{"elements":[
           {"name": "nameA", "VPCZoneIdentifier": "valueA"}, 
           {"name": "nameB", "VPCZoneIdentifier": "valueB"}, 
           {"name": "nameC", "VPCZoneIdentifier": "valueC"}
       ]}'

test and match fn are only available on v1.5.

jq170727
  • 13,159
  • 3
  • 46
  • 56
mruellan
  • 377
  • 4
  • 14

2 Answers2

5

This is a little tricky, but it can be done with reduce. The whole thing could look like this:

selected_subnets_json=$(echo "\"$selected_subnets\"" | jq -c -M 'split(" ")')
echo "$input" | jq -M '.elements = [.elements[] | select(.VPCZoneIdentifier as $id | '"$selected_subnets_json"' | reduce .[] as $v (false; . or $id == $v))]'

The first part makes a JSON array from the shell list:

$ echo "\"$selected_subnets\"" | jq -c -M 'split(" ")'
["valueA","valueB"]

The second part uses the reduce filter to compare the .VPCZoneIdentifier property with all elements of this array. With the selected_subnets_json variable expanded into it, the filter looks as follows:

.elements = [
  .elements[] |
    select(.VPCZoneIdentifier as $id |
           [ "valueA", "valueB" ] | reduce .[] as $v (false; . or $id == $v))
]

That is to say, the elements property is overwritten with those elements of it that match the selection criterium

.VPCZoneIdentifier as $id | [ "valueA", "valueB" ] | reduce .[] as $v (false; . or $id == $v))

Of this the first part remembers the VPCZoneIdentifier as $id (because . will shortly mean something entirely different), and

[ "valueA", "valueB" ] | reduce .[] as $v (false; . or $id == $v))

is an or-reduction of the subnet array. It expands to false or $id == "valueA" or $id == "valueB" in this case.

If you prefer to have it all in one go, you could write

echo "$input" | jq -M '.elements = [.elements[] | select(.VPCZoneIdentifier as $id | ("'"$selected_subnets"'" | split(" ")) | reduce .[] as $v (false; . or $id == $v))]'

This works essentially the same way, except the splitting of $selected_subnets is done inline.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
4

You can set variables from the command line to be available in your queries using the --arg option. You can then filter out the elements using the select filter. Given an array of values, you could do a "value in array" test doing the following:

value == (array[])

So your filter would have the following structure:

.elements | map(
    select(
        .VPCZoneIdentifier == ($subnets | split(" ")[])
    )
)

Putting it all together with your variables:

$ echo $input | jq --arg subnets "$selected_subnets" '.elements | map(select(.VPCZoneIdentifier == ($subnets | split(" ")[])))'
[
  {
    "name": "nameA",
    "VPCZoneIdentifier": "valueA"
  },
  {
    "name": "nameB",
    "VPCZoneIdentifier": "valueB"
  }
]
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272