I need very often update conditionally some fields in subtree to which leads ugly path. I kinda can solve it, but it’s very ugly solution, I’d like to know if there is smth better.
Sample input:
{
"very": {
"un-fortunate": {
"p-a-t-h": {
"first": {
"veryDeepSubTree": {},
"someValue": 1
},
"second": {
"veryDeepSubTree": {},
"someValue": 2
},
"third": {
"veryDeepSubTree": {},
"someValue": 3
}
}
}
}
}
desired output: say that I would like to add 10 to someValue. Suppose that I cannot use recursive descent, for example I want to update only odd someValue.
I can write solution like this:
jq '.very|=(.["un-fortunate"]|=(.["p-a-t-h"] |=( . as $root| reduce keys[] as $k ({}; . + {($k): ($root|.[$k]|.someValue|=(.+10))}) )))'
but it’s hard to read and write (matching brackets)…
explained motivation, based on my (mis?)understanding:
- iiuc I need to bracket every non-trivial right operand of |= , especially if there are more of them, otherwise it does not compile
- I cannot express longer path than just one field at a time, if path is sufficiently ugly
- there isn’t other way(maybe except foreach, which I cannot understand) to update all items of object, than using reduce to copy all into new updating value, and update each part in update clause of reduce command
questions:
- is there some better way of expressing unfortunate path from tree root?
- can I express it somehow easier than via using this reduce over keys, and putting field update logic into accumulator part of reduce command?
- if you are writing commands into shell, how do you do it so that you can easily edit it + it’s readable?
>Solution :
You could use the array value iterator [] in combination with the (arithmetic) update-assignment operator |=.
To update all someValues:
.very."un-fortunate"."p-a-t-h"[].someValue |= . + 10
or
.very."un-fortunate"."p-a-t-h"[].someValue += 10
To update only someValues which are odd, filter through select and don’t forget to parenthesize the LHS):
(.very."un-fortunate"."p-a-t-h"[].someValue | select(. % 2 != 0)) += 10
Output:
{
"very": {
"un-fortunate": {
"p-a-t-h": {
"first": {
"veryDeepSubTree": {},
"someValue": 11
},
"second": {
"veryDeepSubTree": {},
"someValue": 2
},
"third": {
"veryDeepSubTree": {},
"someValue": 13
}
}
}
}
}
But even recursive descent would work, you just have to make sure to only select the correct values:
(.. | objects | .someValue | values | select(. % 2 != 0)) += 10
or
(.. | .someValue? | values | select(. % 2 != 0)) += 10
Output is identical to above.