The idea is that I have an object that is part of an array that itself is the value of a parent key. I want to use the value of that key to alter the values in the original child object.
This is what I came up with, and while it works, I can't help but think there is a better way. It's so unintuitive, it took me at least 3 hours of trial end error to nail down the syntax and remove errors.
echo '{"red": [{"fruit": "apple", "grows_on":"tree"}, {"fruit":"raspberry","grows_on":"vine"}], "green": [{"fruit": "apple", "grows_on":"tree"}, {"fruit":"gooseberry","grows_on":"bush"}]}' |
jq '
walk(
if type=="object" then
with_entries(
if .key == "red" then
with_entries(
walk(
if type=="object" then
with_entries(
if .key=="fruit" then
.value= "red" "_" .value
else . end
)
else . end
)
)
elif .key == "green" then
with_entries(
walk(
if type=="object" then
with_entries(
if .key=="fruit" then
.value= "green" "_" .value
else . end
)
else . end
)
)
else . end
)
else . end
)'
{
"red": [
{
"fruit": "red_apple",
"grows_on": "tree"
},
{
"fruit": "red_raspberry",
"grows_on": "vine"
}
],
"green": [
{
"fruit": "green_apple",
"grows_on": "tree"
},
{
"fruit": "green_gooseberry",
"grows_on": "bush"
}
]
}
CodePudding user response:
You could use with_entries
(which itself is a shortcut to to_entries | map(…) | from_entries
) to deconstruct each field into a key-value pair, manipulate to your liking with access to key and value, and reconstruct it again.
The inner filter saves the key into a variable, descends into each value's fruit
and updates |=
its name, according to the key and its cuurent name .
.
jq 'with_entries(.key as $key | .value[].fruit |= $key "_" .)'
{
"red": [
{
"fruit": "red_apple",
"grows_on": "tree"
},
{
"fruit": "red_raspberry",
"grows_on": "vine"
}
],
"green": [
{
"fruit": "green_apple",
"grows_on": "tree"
},
{
"fruit": "green_gooseberry",
"grows_on": "bush"
}
]
}
If you only want to update a top-level object's fields named red
or green
, and the value array's object item's field named fruit
(this is what your checks are doing), you can employ objects
(which is a shortcut to select(type == "object")
) in combination with appropriate checks.
jq '
objects |= with_entries(.key as $key | if IN("red", "green"; $key) then
(.value[] | objects.fruit // empty) |= $key "_" .
else . end)
'