user4918296
user4918296

Reputation:

Delete json array elements based on values of subarray

I have the following json file which contains this array structure:

{
  "outer": [
    {
      "inner": [
        {
          "value": "val1"
        },
        {
          "value": "val3"
        }
      ]
    },
    {
      "inner": [
        {
          "value": "val2"
        },
        {
          "value": "val1"
        }
      ]
    },
    {
      "inner": [
        {
          "value": "val2"
        },
        {
          "value": "val1"
        },
        {
          "value": "val3"
        }
      ]
    }
  ]
}

I want to delete the inner array from the outer array whose elements have specific values and and is of certain length. E.g., if I want to delete the inner array which contains values "val1" and "val2" the result should be:

{
  "outer": [
    {
      "inner": [
        {
          "value": "val1"
        },
        {
          "value": "val3"
        }
      ]
    },
    {
      "inner": [
        {
          "value": "val2"
        },
        {
          "value": "val1"
        },
        {
          "value": "val3"
        }
      ]
    }
  ]
}

I have tried

jq 'del( .outer[]|select(.inner[0].value == "val1"))'

but I do not know how to check for the second condition, the length and on top of that the values may appear in any order.

Upvotes: 0

Views: 1198

Answers (2)

peak
peak

Reputation: 116880

Here is a solution which should work on all versions of jq at least from version 1.3 onwards, and which is readily adapted to take into account additional criteria, as mentioned in the Q:

# A helper function for defining the retention criteria.
# It is assumed that the input is the array to be checked and that
# `match` is already sorted.
def retain( match ): (map(.value) | sort) != match;

.outer |= map( select( .inner | retain( ["val1", "val2"] ) ))

Upvotes: 1

axiac
axiac

Reputation: 72266

The jq filter you are looking for is:

del(.outer[] | select(.inner | map(.value) | sort == ["val1", "val2"]))

.inner | map(.value) produces an array that contains the values associated to the value key from all objects contained by .inner.

sort is needed because == does a one-to-one comparison of arrays. This way it matches the objects contained in .inner no matter their order. Of course, you have to use a sorted array on the right-hand side (i.e. ["val1", "val2"] and not ["val2", "val1"]).

See it in action.

Upvotes: 1

Related Questions