jq find keys whose value is an array containing a specific element

3.7k views Asked by At

the file is

{
    "ContentKey--4-0-47--Vovb1BQ": ["infra", "qa", "qa-ContentKey-4-0-47-Vovb1BQ", "internal-qa-Conten-WebServi-19E4PUWHRGD44-460820639.us-east-1.elb.amazonaws.com", "plan--default"],
    "ContentKey--4-0-47--zjOkiQ": ["svc", "dev", "dev-ContentKey-4-0-47-zjOkiQ", "dev-Conte-WebServi-KXJXZBDY113W-2116785917.us-east-1.elb.amazonaws.com", "plan--default"],
    "IdGenService--2001-4-22--CJUFaMQ": ["svc", "dev", "dev-IdGenService-2001-4-22-CJUFaMQ", "dev-IdGen-WebServi-R7RVXSYAV92W-304073075.us-east-1.elb.amazonaws.com"],
    "IdGenService--2001-4-22--Uhf9CTQ": ["svc", "qa", "qa-IdGenService-2001-4-22-Uhf9CTQ", "internal-qa-IdGenS-WebServi-RT5BI5EEVZP3-665537643.us-east-1.elb.amazonaws.com"]
}

I want to find the list of keys whose array value have the entry svc

i could get the following to work

cat list.json | jq '. | map(select (. | contains(["svc"])))'

But the output is the value array and not the key itself

[
  [
    "svc",
    "dev",
    "dev-ContentKey-4-0-47-zjOkiQ",
    "dev-Conte-WebServi-KXJXZBDY113W-2116785917.us-east-1.elb.amazonaws.com",
    "plan--default"
  ],
  [
    "svc",
    "dev",
    "dev-IdGenService-2001-4-22-CJUFaMQ",
    "dev-IdGen-WebServi-R7RVXSYAV92W-304073075.us-east-1.elb.amazonaws.com"
  ],
  [
    "svc",
    "qa",
    "qa-IdGenService-2001-4-22-Uhf9CTQ",
    "internal-qa-IdGenS-WebServi-RT5BI5EEVZP3-665537643.us-east-1.elb.amazonaws.com"
  ]
]
2

There are 2 answers

2
Jeff Mercado On BEST ANSWER

The top-level object in your json is an object, not an array. So .[] would only yield its values and discard the keys. Use with_entries/1 to filter that object. This converts an object to an array of key/value pairs and back with which you can apply filters to.

$ jq --arg key 'svc' 'with_entries(select(any(.value[]; . == $key)))' list.json

Also, you should avoid using contains/1 here. It's applied recursively so it will also match strings that contain the substring svc. i.e., "Foosvcbar" will be matched.

0
peak On

With your input, the following filter yields the output as shown:

to_entries[] | select( .value | index("svc") ) | .key

Output:

"ContentKey--4-0-47--zjOkiQ"
"IdGenService--2001-4-22--CJUFaMQ"
"IdGenService--2001-4-22--Uhf9CTQ"

In cases like this, using index/1 is both simpler and (potentially much) faster than using any/2.