Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using jq to select a parent by searching child key/value pair

Tags:

json

bash

select

jq

Using jq, how do I select a parent object if it contains a child object that meets two filter requirements?

In this example I want to select all Subnets elements that have a child tag with key "Name" and value "TheName". My example has two subnets. The first has "TheName" in the wrong key. The second subnet has the name/value pair I am looking for. i.e. "Key": "Name", "Value": "TheName"

The following selects a subnet with the specified value in one of the tags but not the pair. It returns both subnets instead of only the second subnet.

jq '.Subnets[] | select(.Tags[].Value=="TheName")' output

How do I use jq to select only the subnets that have the name/value pair I am looking for?

{
    "Subnets": [
        {
            "VpcId": "vpc-12345678",
            "SubnetId": "subnet-1234567a",
            "Tags": [
                {
                    "Key": "IgnoreThis",
                    "Value": "TheName"
                },
                {
                    "Key": "Name",
                    "Value": "NotTheName"
                }
            ]
        },
        {
            "VpcId": "vpc-12345678",
            "SubnetId": "subnet-1234567b",
            "Tags": [
                {
                    "Key": "IgnoreThis",
                    "Value": "ignore"
                },
                {
                    "Key": "Name",
                    "Value": "TheName"
                }
            ]
        }
    ]
}

The desired output would be:

{
    "VpcId": "vpc-12345678",
    "SubnetId": "subnet-1234567b",
    "Tags": [
        {
            "Key": "IgnoreThis",
            "Value": "ignore"
        },
        {
            "Key": "Name",
            "Value": "TheName"
        }
    ]
}
like image 828
Tim Smith Avatar asked Oct 11 '16 06:10

Tim Smith


2 Answers

Assuming your jq has any/2, a simple and efficient solution would be:

.Subnets[]
| select( any (.Tags[]; .Key == "Name" and .Value == "TheName") )

This produces the output you want, so I won't repeat it here.

If your jq does not have any/2, I'd suggest upgrading, but if that's inconvenient or not an option, you could use this def:

def any(f;g): reduce f as $i (false; . or ($i|g));

p.s. any(str; cond) can be read as: 'Is there any element, e, in the stream, str, such that e|cond has a value other than null or false?'

like image 170
peak Avatar answered Nov 18 '22 11:11

peak


Here is a solution which uses indices

.Subnets[] | select(.Tags | indices({Key:"Name", Value:"TheName"}) != [])
like image 30
jq170727 Avatar answered Nov 18 '22 09:11

jq170727