JQ JSON select with dynamic shell param - json

I use JQ for JSON formatting and filtering the data.
sed "s/.*Service - //p" tes.log | jq 'if (.requests | length) != 0 then .requests |= map(select(.orderId == "2260")) else "NO" end' > ~/result.log
Here, the orderid is been hardcoded to 2260. But my requirement is to make it parameter driven.
So I store the param to a variable called ORDER_ID like,
ORDER_ID=2260
and then using $ORDER_ID, but it doesnt work.
sed "s/.*Service - //p" tes.log | jq 'if (.requests | length) != 0 then .requests |= map(select(.orderId == "$ORDER_ID")) else "NO" end' > ~/result.log
It is not replacing the $ORDER_ID with the passed param.
Thanks

The shell does not expand variables inside single quotes. If jq is agnostic about the quote type, try swapping double and single quotes throughout. Also, if you want to select only the line with Service - in it, you need the -n flag for sed:
sed -n 's/.*Service - //p' tes.log | jq "if (.requests | length) != 0 then .requests |= map(select(.orderId == '$ORDER_ID')) else 'NO' end" > ~/result.log

As the following bash script demonstrates, Stephen Gildea's example will not work because jq does not allow use of ' single quotes.
#!/bin/bash
ORDER_ID=2260
jq "if (.requests | length) != 0 then .requests |= map(select(.orderId == '$ORDER_ID')) else 'NO' end" <<EOF
{"requests":[{"orderId":2260}]}
EOF
Specifically the 'NO' literal will generate a syntax error:
jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?) at <top-level>, line 1:
if (.requests | length) != 0 then .requests |= map(select(.orderId == '2260')) else 'NO' end
jq: error: Possibly unterminated 'if' statement at <top-level>, line 1:
if (.requests | length) != 0 then .requests |= map(select(.orderId == '2260')) else 'NO' end
jq: 2 compile errors
One approach which will work is to use the --argjson option, use single quotes for the entire jq filter and double quotes for strings literals within the filter. E.g.
jq --argjson ORDER_ID "$ORDER_ID" '
if (.requests|length)!=0 then .requests |= map(select(.orderId == $ORDER_ID)) else "NO" end
'

Related

jq: partition based on a filter

Let's say I have the following:
jq 'map(select(. >= 5))'
given [1,2,3,4,5,6,7] it returns:
[5,6,7]
I also have
jq 'map(select(. < 5))'
which given the same data, returns [1,2,3,4]. How can I do these complementary queries at the same time, so that I receive for example:
[1,2,3,4], [5,6,7]
jq has a built-in filter for grouping by some (possibly multi-valued) criterion:
jq -nc '[1,2,3,4,5,6,7] | group_by(. < 5)'
produces:
[[5,6,7],[1,2,3,4]]
One option is to use reduce:
reduce .[] as $x
([]; if $x < 5 then .[0] += [$x] else .[1] += [$x] end)
This will produce:
[[1,2,3,4],[5,6,7]]

jq to_entries string and number cannot be added

I'm not understanding how to_entries works in jq.
I have the following json payload in payload.json
{"REGION":"us-east-1","EMAIL":"contain","UPDATE":1}
which I want to convert into = delimited keypairs, like so;
REGION=us-east-1
EMAIL=contain
UPDATE=1
I was using
jq -r 'to_entries | .[] | .key + "=" + .value' < payload.json
But I get an error
jq: error (at <stdin>:0): string ("UPDATE=") and number (1) cannot be added
If I understand correctly, the issue is that the update value is a number, not a string (ie, having them not match types is an issue) so I tried the following, both with the same error;
string interpolation:
jq -r 'to_entries | .[] | (.key) + "=" + (.value)' < payload.json
tostring:
jq -r 'to_entries | .[] | .key + "=" + .value|tostring' < payload.json
What am I missing?
what am I missing?
A pair of parentheses:
.key + "=" + ( .value|tostring )
Alternatively, you could use string interpolation, e.g.
"\(.key)=\(.value)"

jq split string and assign

I have the following json
{
"version" : "0.1.2",
"basePath" : "/"
}
and the desired output is
{
"version" : "0.1.2",
"basePath" : "beta1"
}
I have the following jq which is producing the error below:
.basePath = .version | split(".") as $version | if $version[0] == "0" then "beta"+ $version[1] else $version[0] end
jq: error (at :3): split input and separator must be strings
exit status 5
Using .basePath = .version assigns the value successfully and .version | split(".") as $version | if $version[0] == "0" then "beta"+ $version[1] else $version[0] end on its own returns "beta1". Is there a way to assign the string to the basePath key?
Good news! Your proposed solution is just missing a pair of parentheses. Also, there is no need for $version. That is, this will do it:
.basePath = (.version | split(".")
| if .[0] == "0" then "beta"+ .[1] else .[0] end)

How to print out the top-level json after modification of descendants

Hello i managed to create this jq filter .profiles | recurse | .gameDir? | if type == "null" then "" else . end | scan("{REPLACE}.*") | sub("{REPLACE}"; "{REPLACESTRINGHERE}"). it succesfully replaces what i want (checked at jqplay.org) but now i'd like to print the full json and not just the modified strings
Adapting your query:
.profiles |= walk( if type == "object" and has("gameDir")
then .gameDir |=
(if type == "null" then "" else . end
| scan("{REPLACE}.*") | sub("{REPLACE}"; "{REPLACESTRINGHERE}"))
else .
end )
(This can easily be tweaked for greater efficiency.)
If your jq does not have walk, you can google it (jq “def walk”) or snarf its def from the jq FAQ https://github.com/stedolan/jq/wiki/FAQ
walk-free approach
For the record, here's an illustration of a walk-free approach using paths. The following also makes some changes in the computation of the replacement string -- notably it eliminates the use of scan -- so it is not logically equivalent, but is likely to be more useful as well as more efficient.
.profiles |=
( . as $in
| reduce (paths | select(.[-1] == "gameDir")) as $path ($in;
($in | getpath($path)
| if type == "null" then ""
else sub(".*{REPLACE}"; "{REPLACESTRINGHERE}")
end) as $value
| setpath($path; $value) ))

jq: how to only update data if existing?

With dev version of jq, this could be done with jq '.x.y |= if . then 123 else empty end'. (Because bug #13134 is solved.)
How can I do this in jq 1.5?
example:
in {"x": {"y": 5}}, y should be changed to 123,
but in {"x": {"z": 9}}, nothing should change.
do you need to use |=? If not could you use ordinary assignment? e.g.
jq -Mnc '
{"x": {"y": 5}} | if .x.y != null then .x.y = 123 else . end
, {"x": {"z": 9}} | if .x.y != null then .x.y = 123 else . end
'
output
{"x":{"y":123}}
{"x":{"z":9}}
With built-in has() function:
jq -nc '{"x":{"y": 5}} | if (.x | has("y")) then .x.y=123 else empty end'
The output:
{"x":{"y":123}}
Both the following produce the desired results (whether using 1.5 or later), but there are important differences in the semantics (having to do with the difference between {"x": null} and {}):
if has("x") and (.x | has("y")) then .x.y = 123 else . end
if .x.y? then .x.y = 123 else . end
Using streams could actually handle this quite nicely. A stream for an object will yield paths and values to actual existing values in your input. So search for the pairs that contain your path and update the value while rebuilding the stream.
$ jq --argjson path '["x","y"]' --argjson new '123' '
fromstream(tostream|select(length == 2 and .[0] == $path)[1] = $new)
' input.json