Johannes Riecken
Johannes Riecken

Reputation: 2515

Filtering deeply within tree

I'm trying to prune nodes deeply within a JSON structure and I'm puzzled why empty behaves seemingly different from a normal value here.

Input

[
    {
        "name": "foo",
        "children": [{
            "name": "foo.0",
            "color": "red"
        }]
    },
    {
        "name": "bar",
        "children": [{
            "name": "bar.0",
            "color": "green"
        },
        {
            "name": "bar.1"
        }]
    },
    {
        "name": "baz",
        "children": [{
            "name": "baz.0"
        },
        {
            "name": "baz.1"
        }]
    }
]

Program

jq '(.[].children|.[])|=if has("color") then . else empty end' foo.json

Actual output

[
  {
    "name": "foo",
    "children": [
      {
        "name": "foo.0",
        "color": "red"
      }
    ]
  },
  {
    "name": "bar",
    "children": [
      {
        "name": "bar.0",
        "color": "green"
      }
    ]
  },
  {
    "name": "baz",
    "children": [
      {
        "name": "baz.1"
      }
    ]
  }
]

Expected output

The output I get, except without the baz.1 child, as that one doesn't have a color.

Question

Apart from the right solution, I'm also curious why replacing empty in the script by a regular value like 42 would replace the children without colors with 42 as expected, but when replacing with empty, it looks like the else branch doesn't get executed?

Upvotes: 0

Views: 96

Answers (2)

0stone0
0stone0

Reputation: 43983

.[].children |= map(select(.color))

Will remove children that does not has an color so the output becomes:

[
  {
    "name": "foo",
    "children": [
      {
        "name": "foo.0",
        "color": "red"
      }
    ]
  },
  {
    "name": "bar",
    "children": [
      {
        "name": "bar.0",
        "color": "green"
      }
    ]
  },
  {
    "name": "baz",
    "children": []
  }
]
Online demo

Regarding why your filter does not seem to like empty;
This git issue seems to be the cause, multiple elements with empty will fail.

Upvotes: 1

oguz ismail
oguz ismail

Reputation: 50775

There must be a bug with assigning empty to multiple paths.

In this case you can use del instead:

del(.[].children[] | select(has("color") | not))

Online demo

Upvotes: 1

Related Questions