2

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
  • .first_level."second_level"[] | select(.fruit == "pear") Commented May 25, 2020 at 18:21

1 Answer 1

2

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"))
Sign up to request clarification or add additional context in comments.

1 Comment

Thank! I now understand why this code doesn't behave like I would expect. Thanks so much for the clarification and for the sample you've provided! :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.