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"
}
]
}
I tried below command which is updating existing value but not appending for new values.
*jq --arg updateName "user4" --arg updateVal "root" '.environment = [.environment[] | if (.name == $updateName) then (.value = $updateVal) elif (.name != $updateName) then (. = [{"name": $updateName, "value": $updateVal}] ) else . end]' envt.json*
Below is the error
jq: error (at envt.json:18): object ({"name":"Us...) and array ([{"name":"us...)
cannot be added
Can anyone please suggest me on how to achieve this.
CodePudding user response:
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
CodePudding user response:
Do you care about the order of the elements of the array to be modified?
No, the array is unordered
Take those elements that do not match (map(select(.name != $name))
) and unconditionally add the new one to it ( [{$name,$value}]
). That way, a matching element gets deleted if and only if it exists, and in any case the new one is added. The added or updated element will always be the last element.
jq --arg name "user4" --arg value "root" '
.environment |= map(select(.name != $name)) [{$name,$value}]
' envt.json
Yes, the array is ordered
If the array is ordered, only a newly created should be added to the end of the array, while updated elements should be replaced at their original position in the array.
For this, we don't update (|=
) the array itself as a whole but those elements that matter. If elements matching the condition exist ((.[] | select(.name == $name))
), they matter, otherwise (//
) a (new) element at position length
(past the end of the array, as indices go from 0
to length-1
) is the one that matters. Those and only those will be updated wit 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 variables to $name
and $value
(the future field names) in order to simplify creating the new object as {$name,$value}
.