Select items in array based on a property value

Viewed 81

As a developper, when I use the following JQ command:

.first_level."second_level"[] |= select(.fruit == "pear")

And I apply it to this JSON:

{
  "first_level": {
    "second_level": [
      {
        "fruit": "apple"
      },
      {
        "fruit": "pear"
      },
      {
        "fruit": "banana"
      },
      {
        "fruit": "donuts"
      }
    ]
  }

}

Then I would like to have this output (only keep the items that have the property fruit equal to pear):

{
  "first_level": {
    "second_level": [
      {
        "fruit": "pear"
      }
    ]
  }

}

But the current value that is returned is (can also be seen in JQ Play here):

{
  "first_level": {
    "second_level": [
      {
        "fruit": "pear"
      },
      {
        "fruit": "donuts"
      }
    ]
  }
}

--> It keeps the value with donuts and what I don't understand is that from the JQ documentation, the |= should assign the value of the selection but when running:

.first_level."second_level"[] | select(.fruit == "pear")

I've the following result:

{
  "fruit": "pear"
}

--> It seems to select succesfully but the affectation doesn't behave as I expect (it adds the donuts).

Any help with this issue would be greatly appreciated. Thanks in advance! :)

1 Answers

Given your sample input what that program does is basically this:

.first_level.second_level |= (
    delpaths([[0]]) # apple != pear
  | delpaths([[1]]) # banana != pear
  | delpaths([[2]]) # null != pear
  | delpaths([[3]]) # null != pear
)

See how the underlying builtin for |= is implemented here; when the righthand side of |= applied to a path's value, if the result is empty, that path is deleted by means of delpaths. So the problem is clear, when .[0] (apple) is deleted, .[1] (pear) becomes .[0]; but _modify doesn't take that change into account as it takes the list of paths to be modified once at the beginning and doesn't update again, and continues with .[1] (banana).

You can use this instead:

del(.first_level.second_level[] | select(.fruit != "pear"))

Or if you insist on a solution involving |=:

.first_level.second_level |= map(select(.fruit == "pear"))
Related