user8914241
user8914241

Reputation:

jq recursively delete values that appear in array anywhere in schema

I'm trying to find out how to use jq to remove a certain value that occurs anywhere in the schema in an array.

In this case i'm trying to remove agent4 from anywhere inside an array underneath the field labelled agents.

This is what I have so far

jq '..|.agents? | select(. != null) |  map(select(. != "agent4"))'

But that just creates the changed data and I don't know how to re-assign it to the path.

I originally tried using sed for this but its definitely not the tool to use so I switched to jq.

   {
      "environments": {
        "default": {
          "machines": {
            "dev-machine": {
              "agents": [
                "agent1",
                "agent2",
                "agent3",
                "agent4"
              ]
            }
          }
        }
      },
      "environments2": {         
         "agents": [
            "agent1",
            "agent2",
            "agent3",
            "agent4"
         ] 
      }
    }

However this just outputs

[
  "agent1",
  "agent2",
  "agent3"
]
[
  "agent1",
  "agent2",
  "agent3"
]

Upvotes: 4

Views: 1226

Answers (2)

jq170727
jq170727

Reputation: 14695

Here is a solution which uses path to find "agent4" and verify it appears within "agents" before removing it with delpaths

delpaths([ path(..|select(.=="agent4")) | select(.[-2]=="agents") ])

Sample Run (assumes data in data.json)

$ jq -M 'delpaths([ path(..|select(.=="agent4")) | select(.[-2]=="agents") ])' data.json
{
  "environments": {
    "default": {
      "machines": {
        "dev-machine": {
          "agents": [
            "agent1",
            "agent2",
            "agent3"
          ]
        }
      }
    }
  },
  "environments2": {
    "agents": [
      "agent1",
      "agent2",
      "agent3"
    ]
  }
}

Try it online at jqplay.org

Here is another way using reduce, leaf_paths, getpath and delpaths:

reduce leaf_paths as $p (.;
  if $p[-2]=="agents" and getpath($p)=="agent4" then delpaths([$p]) else . end
)

Try it online at jqplay.org

Upvotes: 0

peak
peak

Reputation: 116900

Here's a solution using walk:

walk( if type == "object" and has("agents") 
      then .agents |= map(select(. != "agent4"))
      else . end )

If you want to remove the value from all arrays, wherever they occur:

walk( if type == "array" then map(select(. != "agent4")) else . end )

If you want a more flexible solution, you could, for example, replace "agent4" by $value, and then set $value on the command-line, e.g. using --arg value VALUE if VALUE is a string, or --argjson value VALUE as appropriate.

If your jq does not have walk, simply prepend its definition, which is available (for example) from https://github.com/stedolan/jq/blob/master/src/builtin.jq

Upvotes: 2

Related Questions