Reputation: 7526
Below is a sample output that is returned when calling an API:
curl "https://mywebsite.com/api/cars.json&page=1" | jq '.'
Using jq
, how would one count the number or records where the charge
key is missing? I understand that the first bit of code would include jq '. | length'
but how would one filter out objects that contain or don't contain a certain key value ?
If applied to the sample below, the output would be 1
{
"current_page": 1,
"items": [
{
"id": 1,
"name": "vehicleA",
"state": "available",
"charge": 100
},
{
"id": 2,
"name": "vehicleB",
"state": "available",
},
{
"id": 3,
"name": "vehicleB",
"state": "available",
"charge": 50
}
]
}
Upvotes: 8
Views: 7845
Reputation: 116919
Here is a solution that uses a simple but powerful utility function worthy perhaps of your standard library:
def sigma(stream): reduce stream as $s (null; . + $s);
The filter you'd use with this would be:
sigma(.items[] | select(has("charge") == false) | 1)
This is very efficient as no intermediate array is required, and no useless additions of 0 are involved. Also, as mentioned elsewhere, using has
is more robust than making assumptions about the value of .charge
.
If you have no plans to use jq's module system, you can simply add the above definition of sigma
to the file ~/.jq and invoke jq like so:
jq 'sigma(.items[] | select(has("charge") == false) | 1)'
Better yet, if you also add def count(s): sigma(s|1);
to the file, the invocation would simply be:
jq 'count(.items[] | select(has("charge") | not))'
If for example ~/.jq/jq/jq.jq is your standard library, then assuming count/1
is included in this file, you could invoke jq like so:
jq 'include "jq"; count(.items[] | select(has("charge") == false))'
Upvotes: 3
Reputation: 14715
Here is a solution using map
and length
:
.items | map(select(.charge == null)) | length
Here is a more efficient solution using reduce
:
reduce (.items[] | select(.charge == null)) as $i (0;.+=1)
Sample Run (assuming corrected JSON data in data.json
)
$ jq -M 'reduce (.items[] | select(.charge == null)) as $i (0;.+=1)' data.json
1
Note that each of the above takes a minor shortcut assuming that the items won't have a "charge":null
member. If some items could have a null
charge then the test for == null
won't distinguish between those items and items without the charge
key. If this is a concern the following forms of the above filters which use has
are better:
.items | map(select(has("charge")|not)) | length
reduce (.items[] | select(has("charge")|not)) as $i (0;.+=1)
Upvotes: 17