Reputation: 2587
I get Cannot iterate over null (null)
from the below query because .property_history
is not present in result
object.
How do I check for the presence of .property_history
key before proceeding with map(...)
?
I tried using something like
sold_year= `echo "$content" | jq 'if has("property_history") then
map(select(.event_name == "Sold"))[0].date' else null end
Original Query:
sold_year=`echo "$content" | jq '.result.property_history | map(select(.event_name == "Sold"))[0].date'`
JSON:
{
"result":{
"property_history":[
{
"date":"01/27/2016",
"price_changed":0,
"price":899750,
"event_name":"Listed",
"sqft":0
},
{
"date":"12/15/2015",
"price_changed":0,
"price":899750,
"event_name":"Listed",
"sqft":2357
},
{
"date":"08/30/2004",
"price_changed":0,
"price":739000,
"event_name":"Sold",
"sqft":2357
}
]
}
}
Upvotes: 116
Views: 175893
Reputation: 803
use has("mykey1")
(for objects) or has(0)
(for arrays):
jq 'has("name")' <<< '{"name": "hello"}'
output:
true
Upvotes: 41
Reputation: 16806
The shortest you can use (with the bonus that the exit code will be set):
# Example that exits with code 0
jq -e 'has("result")'
# Example that exists with code 0
jq -e '.result[].property_history | has("price")'
# Example that exists with code 1
jq -e '.result[].property_history | has("dummyKey")'
In these examples, because of setting the flag -e
, if has
returned true, the exit code will be set to 0, otherwise 1. Which saves unnecessary additional steps.
Upvotes: 6
Reputation: 116690
Technically, to test for the presence of a property, you should use has/1
, but in the present context, it would probably be better to use the postfix ?
operator, e.g.:
$ jq '.result
| .property_history[]?
| select(.event_name == "Sold")
| .date'
"08/30/2004"
If there is a possibility that the value of .result is not a JSON object, then you could replace the second line above by:
try(.property_history[])
Upvotes: 40
Reputation: 7309
General pattern:
try (...) // "default_value"
With your logic:
jq 'try (.result.property_history | map(select(.event_name == "Sold"))[0].date) // "default_value"'
try
(without a catch) returns empty if the expression fails. //
provides a default value if the value is empty.
Upvotes: 7
Reputation: 927
The trick is to use // together with empty:
jq '.result.property_history // empty | map(select(.event_name == "Sold"))[0:1][].date'
Another alternative is to use an additional select:
jq '.result.property_history | select(.) | map(select(.event_name == "Sold"))[0:1][].date'
Upvotes: 5
Reputation: 85560
You can use the select-expression in jq
to do what you intend to achieve, something as,
jq '.result
| select(.property_history != null)
| .property_history
| map(select(.event_name == "Sold"))[0].date'
Upvotes: 124