Using jq, how to append an element to an array without it updating every other object?

Viewed 205

I have a test.json file that looks like this:

[
  {
    "array": [
      "string1",
      "string2"
    ]
  },
  {
    "object1": {
      "subobject1": {},
      "subobject2": {}
    }
  },
  {
    "object2": {}
  }
]

I'm trying to append an element, string3 into array. I've tried the following commands:

$ jq '.[] | .array |= . + ["string3"]' test.json
$ jq '.[] | .array += ["string3"]' test.json

What I expect is this, string3 only being appended to array:

{
  "array": [
    "string1",
    "string2",
    "string3"
  ]
}
{
  "object1": {
    "subobject1": {},
    "subobject2": {}
  },
}
{
  "object2": {},
}

However, I get these as the actual result, where the array array is being added to all objects:

{
  "array": [
    "string1",
    "string2",
    "string3"
  ]
}
{
  "object1": {
    "subobject1": {},
    "subobject2": {}
  },
  "array": [
    "string3"
  ]
}
{
  "object2": {},
  "array": [
    "string3"
  ]
}

How can I append to the one array array without affecting any other objects in the file?

I'm using jq version 1.6.

3 Answers

Well, does something like this work for you?

jq '.[0].array += ["string3"]' test.json

I see this response, which looks pretty much like what I would want:

[
  {
    "array": [
      "string1",
      "string2",
      "string3"
    ]
  },
  {
    "object1": {
      "subobject1": {},
      "subobject2": {}
    }
  },
  {
    "object2": {}
  }
]

EDIT 1: I like the more general solutions provided by @peak particularly the first one:

map( if .array then .array += ["string3"] else . end)

Here's a snippet you can experiment with. It's easy to see how you could insert the new string into the second contained array rather than the first with:

 map( if .array2 then .array2 += ["string3"] else . end)

Depending on your requirements, you may wish to consider:

map( if .array then .array += ["string3"] else . end)

or even:

walk( if type == "object" and .array then .array += ["string3"] else . end)

If you only want the first occurrence at the top-level to be affected:

(first(.[]|.array // empty)) += ["string3"]

You can try this to find object by key array and append there

jq '.[] | select(has("array")).array += ["string3"]' test.json

This is what the response looks like

{
  "array": [
    "string1",
    "string2",
    "string3"
  ]
}
{
  "object1": {
    "subobject1": {},
    "subobject2": {}
  }
}
{
  "object2": {}
}
Related