faceless
faceless

Reputation: 488

JQ does not contain for "key": "value" JSON structure

The question is similar to THIS, but the JSON structure is different.

In my usecase JSON has an arrays with key:value data:

{
   "data":[
      {
         "name":"banana",
         "tags":[
            {
               "id":"yellow"
            },
            {
               "id":"long"
            }
         ]
      },
      {
         "name":"apple",
         "tags":[
            {
               "id":"red"
            },
            {
               "id":"round"
            }
         ]
      },
      {
         "name":"orange",
         "tags":[
            {
               "id":"orange"
            },
            {
               "id":"round"
            },
            {
               "id":"colored"
            }
         ]
      }
   ]
}

What required is to filter the only elements that do not have certain keyword - "red" for instance.

When i use jq '.data[] | select(.tags[].id | index( "red" ))' it brings me the correct resut of 'apple' (as it has "id": "red") and 'orange' (as it has "id": "colored").

However, when i add the negation jq '.data[] | select(.tags[].id | index( "red" ) | not)' the results are more than strange, with elements' duplication, totally enigmatic.

How can i use jq to filter the result the way it returns only elements that do not have the exact match among the array values?

Upvotes: 1

Views: 1253

Answers (2)

0stone0
0stone0

Reputation: 43904

index works on an array, your current filter does not pass index to an array, therefore you're getting other results then expected.


.data[] | select([ .tags[].id ] | index("red") | not)

Here we create an array with all the id's [ .tags[].id ] and use that array to check for red: | index("red") | not


The above filter gives the following output:

{
  "name": "banana",
  "tags": [
    {
      "id": "yellow"
    },
    {
      "id": "long"
    }
  ]
}
{
  "name": "orange",
  "tags": [
    {
      "id": "orange"
    },
    {
      "id": "round"
    },
    {
      "id": "colored"
    }
  ]
}

Demo


If you want to exclude 'colored', use contains():

.data[] | select([ .tags[].id ] | contains(["red"]) | not)
{
  "name": "banana",
  "tags": [
    {
      "id": "yellow"
    },
    {
      "id": "long"
    }
  ]
}

Demo

Upvotes: 2

pmf
pmf

Reputation: 36033

You can use all to give a condition all items must meet. Here, all values in .tags[].id must be unequal != to "red":

jq '.data[] | select(all(.tags[].id; . != "red"))'
{
  "name": "banana",
  "tags": [
    {
      "id": "yellow"
    },
    {
      "id": "long"
    }
  ]
}
{
  "name": "orange",
  "tags": [
    {
      "id": "orange"
    },
    {
      "id": "round"
    },
    {
      "id": "colored"
    }
  ]
}

Demo

Add .name and use the -r option to only get the names:

jq -r '.data[] | select(all(.tags[].id; . != "red")).name'
banana
orange

Demo

Upvotes: 2

Related Questions