Reputation: 1
I have below sample json
{
"environment": [
{
"name": "user1",
"value": "app"
},
{
"name": "user2",
"value": "admin"
},
{
"name": "user3",
"value": "db"
}
]
}
I need to update the value with new value (passed as an input argument to the script) when name exists. If different name and value are given, need to append them as new fields in the end. For example, if name=user4 and value=root then my new json should look like this
{
"environment": [
{
"name": "user1",
"value": "app"
},
{
"name": "user2",
"value": "admin"
},
{
"name": "user3",
"value": "db"
},
{
"name": "user4",
"value": "root"
}
]
}
If existing values given as user3, aws then it has to update the value of user3 as follows
{
"environment": [
{
"name": "user1",
"value": "app"
},
{
"name": "user2",
"value": "admin"
},
{
"name": "user3",
"value": "aws"
}
]
}
Its working as expected but only has the below problem.
When my JSON has any extra tags before environment tag, after execution of command, those are getting trimmed off and next time when I run same command with different inputs, It won't find the previous existing tag and giving this error Cannot iterate over null (null)
With the below command that is given by peak, taskDefinition & containerDefinitions tags are getting trimmed off. Hence please help me to have these tags in place.
jq --arg updateName "user5" --arg updateVal "admin" '
.taskDefinition | .containerDefinitions[] | .environment |=
if any(.[]; .name == $updateName)
then map(if .name == $updateName then .value = $updateVal else . end)
else (. + [{"name": $updateName, "value": $updateVal}] )
end' envt_values1.json
{
"taskDefinition": {
"containerDefinitions": [
{
"environment": [
{
"name": "user1",
"value": "app"
},
{
"name": "user2",
"value": "admin"
},
{
"name": "user3",
"value": "aws"
},
{
"name": "user4",
"value": "root"
},
{}
],
"configuration": []
}
]
}
}
Upvotes: 0
Views: 395
Reputation: 36033
Do you care about the order of the elements in the array to be modified?
Take an array of only those elements that do not match (map(select(.name != $name))
) and unconditionally add an element with the new data to it (+ [{$name,$value}]
). That way, an existing element gets deleted if and only if it matches, and a new one is added in any case. The one added (or updated) will always be at the array's final position, not at any 'deleted position'.
jq --arg name "user4" --arg value "root" '
.environment |= map(select(.name != $name)) + [{$name,$value}]
' envt.json
If the array is ordered, only a truly new element should be added at the end of the array, while updated elements should be replaced at their original positions within the array.
For this, we don't update the whole array as before (.environment |= …
), just certain elements that matter ((.environment | …) |= …
). If there are elements matching the condition ((.[] | select(.name == $name))
), they matter, otherwise (//
) the element at position length
(past the end of the array, thus new, as indices go from 0
to only length-1
) is the one that matters. Whichever elements get through, they will be updated with the data provided (|= {$name,$value}
):
jq --arg name "user4" --arg value "root" '
(.environment | ((.[] | select(.name == $name)) // .[length])) |= {$name,$value}
' envt.json
Sidenote: I also renamed the input variables to $name
and $value
(according to the future field names) in order to simplify creating the new object as {$name,$value}
.
Upvotes: 0
Reputation: 116680
This implements the requirements as I understand them:
jq --arg updateName "user4" --arg updateVal "root" '
.environment |=
if any(.[]; .name == $updateName)
then map(if .name == $updateName then .value = $updateVal else . end)
else (. + [{"name": $updateName, "value": $updateVal}] )
end' input.json
Upvotes: 1