JQ: Remove object from multiple arrays

时间:2016-07-11 19:09:08

标签: arrays json jq

I want to use jq to remove all objects with a given name from all arrays in the input data. For example deleting "Name1" from this:

{
  "Category1": [
    {
      "name": "Name1",
      "desc": "Desc1"
    },
    {
      "name": "Name2",
      "desc": "Desc2"
    }
  ],
  "Category2": [
    {
      "name": "Name1",
      "desc": "Desc1"
    },
    {
      "name": "Name3",
      "desc": "Desc3"
    }
  ],
  "Category3": [
    {
      "name": "Name4",
      "desc": "Desc4"
    }
  ]
}

Should yield this:

{
  "Category1": [
    {
      "name": "Name2",
      "desc": "Desc2"
    }
  ],
  "Category2": [
    {
      "name": "Name3",
      "desc": "Desc3"
    }
  ],
  "Category3": [
    {
      "name": "Name4",
      "desc": "Desc4"
    }
  ]
}

I haven't worked with jq, or indeed JSON, much and after several hours of googling and experimenting I haven't been able to figure it out. How would I do this?

The closest I managed was this:

cat input | jq 'keys[] as $k | .[$k] |= map( select( .name != "Name1"))'

This does filter each of the arrays but returns the result as three separate objects and this is not what I want.

3 个答案:

答案 0 :(得分:0)

Here is a solution that will remove all objects with the specified name, wherever they occur. It uses the generic function walk/1, which is a built-in in versions of jq > 1.5, and can therefore be omitted if your jq includes it, but there is no harm in including it redundantly, e.g. in a jq script.

# Apply f to composite entities recursively, and to atoms
def walk(f):
  . as $in
  | if type == "object" then
      reduce keys[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

walk(if type == "object" and .name == "Name1" then empty else . end)

If you really only want to remove objects from arrays, then you could use:

walk(if type == "array" then map(select( type != "object" or .name != "Name1")) else . end)

答案 1 :(得分:0)

If the structure of your input JSON is always as seen on your example, try this:

map_values(map(select(.name != "Name1")))

答案 2 :(得分:0)

以下是使用 reduce del

的解决方案
reduce keys[] as $k (
  .
; del(.[$k][] | select(.name == "Name1"))
)