skip to Main Content

I have the following JSON input:

{
  "a": {
    "a": "foo",
    "b": "bar"
  },
  "b": {
    "a": [],
    "b": []
  },
  "c": {},
  "d": [
    "foo",
    "bar"
  ]
}

I want to recursively remove all the keys that point at empty arrays or objects, so that the result looks like:

{
  "a": {
    "a": "foo",
    "b": "bar"
  },
  "d": [
    "foo",
    "bar"
  ]
}

What I’ve come up with is:

jq 'walk(if ((type=="object" or type=="array") and length==0) then empty else . end)'

But this deletes everything but the last array:

{
  "d": [
    "foo",
    "bar"
  ]
}

If I change empty to {"A":1}:

jq 'walk(if ((type=="object" or type=="array") and length==0) then {"A":1} else . end)'

I get what I expect, where the first, populated object remains untouched:

{
  "a": {
    "a": "foo",
    "b": "bar"
  },
  "b": {
    "a": {
      "A": 1
    },
    "b": {
      "A": 1
    }
  },
  "c": {
    "A": 1
  },
  "d": [
    "foo",
    "bar"
  ]
}

So why is it when I instead tell it to replace zero-length arrays and objects with empty that it attacks non-zero-length objects? What am I doing wrong?

2

Answers


  1. Here is one way to do it:

    def f: recurse | select(. == [] or . == {});
    until(isempty(f); del(f))
    

    Online demo

    Login or Signup to reply.
  2. remove all the keys that point at empty arrays or objects

    Here’s a solution using walk and IN:

    walk( if type == "object" 
          then with_entries(select(.value | IN([],{}) | not ) ) 
          else . end)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search