How can I count objects in jq (not in an array) - json

I have some json input that comes as a list of objects (not as a json array) like, for which I want to count the number of active, and the number of inactive:
{
"key" : "state",
"value" : "active"
}
{
"key" : "state",
"value" : "active"
}
{
"key" : "state",
"value" : "active"
}
{
"key" : "state",
"value" : "inactive"
}
I want to transform using only JQ (in fact it will be handled by jackson-jq in java code, so I can not use some shell tricks).
I tried many things, like select(.value == "active") | length to get the number of active, but it is always handle object per object.
Even when using reduce, it is always handled object per object.
Example of output is :
2
2
2
(I have 3 objects with 2 fields that match value="active)
The output that I expect is :
3
Here is a playground with my example.

To avoid "slurping" the input, you would use either reduce or foreach. Here's a suitable abstraction built from reduce:
def count(s; f; g):
reduce s as $s ([0,0];
if $s|f then .[0]+=1 else . end
| if $s|g then .[1]+=1 else . end);
With this, you would invoke jq with the -n command-line option and run a query such as:
count(inputs|.value; .=="active"; .=="inactive")
Notice that one cannot use count/1 (defined analogously) twice since inputs consumes the input stream.

Related

Powershell trouble getting value from key named count

I have a simple json that I am trying to get values from in Powershell
$webData = #'
{
"results": [
{
"facet": "266976",
"appid": "266976",
"count": 233206
},
{
"facet": "27096405",
"appid": "27096405",
"count": 85669
}
]
}
'#
$data1 = $webData | ConvertFrom-Json
Write-Output $data1.results.count
When I write output I get values count and not value itself like I do for appid and facet.
Desired result: 233206
Current result: 2
I cannot change json.
Is there a way for PS not to see it as a count operator?
Thank you
As mklement0 notes, in this case the array type's ICollection.Count property takes precedence over member access enumeration of the array-element property named Count.
A simple workaround is to use the intrinsic method .ForEach(string PropertyName):
$data1.results.ForEach('count')
As Santiago Squarzon notes, the type of ForEach() output is Collection`1. If you actually need an array, use the array sub-expression operator #() or use the workaround suggested by Santiago, using GetEnumerator().
$arrayOfCount = #($data1.results.ForEach('count'))
$arrayOfCount = $data1.results.GetEnumerator().Count
Output:
233206
85669
As Santiago Squarzon noted, if you actually want to get the value of count for the first object only, you can write:
$data1.results[0].Count
Output:
233206
Note that Write-Output is effectively redundant, because PowerShell implicitly outputs the "result" of any statement. So I've removed it in the samples above.

In jq, how to select objects where an array contains at least one value (intersection non-empty)

I have input like this:
{ "prop": ["apple", "banana"] }
{ "prop": ["kiwi", "banana"] }
{ "prop": ["cherry", "orange"] }
How do I print objects where prop contains at least one of kiwi and orange?
(The list of interesting values is longer than just 2, so I'd like to leverage the any function somehow.)
I've tried the following
jq 'select(any(.prop[] | contains(["kiwi", "orange"])))' < input.json
and various variants of the above, but can't figure out the right expressions.
The stream-oriented version of the built-in function any can be most easily used if one bears in mind its signature:
def any(generator; condition):
So we are led to:
select( any( .prop[]; . == "kiwi" or . == "orange" ))
or more succinctly:
select( any(.prop[]; IN("kiwi", "orange")))
whitelist
If the values of interest are provided as a JSON array, say $whitelist, you could tweak the above by substituting $whitelist[] for the explicit stream of values:
select( any(.prop[]; IN($whitelist[]) ))
I think you're looking for IN/2. It's implemented using any, but is far easier to grasp.
select(IN(.prop[]; "kiwi", "orange"))
Online demo

Extract all the values for a specific repeated key in JSON

I have a huge json file of about 4500 lines.
I wish to extract the value of all the keys named "value".
The levels of json paths are not same.
JSON Sample :
{
k1:v1,
k2:v2,
k3:v3,
k4:{
k5:v5,
k6:{
k7:v7,
value:"value1"
}
}
k8:v8,
value:"value2"
}
There are multiple such "value" tags.
Is there a way using jq to get all the values ?
Use recursive descent.
.. | objects | if has("value") then .value else empty end
Here is a slightly shorter variation of oguz ismail's answer which uses Optional Object Identifier .foo? and Alternative operator // along with recursive descent.
.. | .value? // empty
Example output (using corrected JSON Sample)
"value2"
"value1"
Note that this shortcut won't produce the same output if the "value" key may be null as // can't distinguish between a null "value" and a null produced by the ? operator when the "value" key is missing. It that's a concern then testing for the presence of the "value" key with has is better.
Try it online!

Is there a way to randomize the jsonPath array number in get[] myJson?

I have a list of values that I can use for the title field in my json request. I would like to store a function in the common.feature file which randomizes the title value when a scenario is executed.
I have attempted using the random number function provided on the commonly needed utilities tab on the readme. I have generated a random number successfully, the next step would be using that randomly gernerated number within the jsonpath line in order to retrieve a value from my data list which is in json.
* def myJson =
"""
{
"title" : {
"type" : "string",
"enum" : [
"MR",
"MRS",
"MS",
"MISS"
[...]
]
}
}
"""
* def randomNumber = random(3)
* def title = get[0] myJson.title.enum
* print title```
The code above works but I would like to randomize the number within the get[0]. How is this possible in Karate?
I'm not sure of what you want, but can't you just replace 0 by randomNumber in get[randomNumber] myJson.title.enum ?

JSONPath regular expression - NOT starting with

My JSON (simplified) looks like this:
[
{"name" : "foobar",
"id" : 123
},
{"name" : "bar",
"id" : 123
},
{"name" : "foobar",
"id" : 456
}, ...
]
I'm using https://jsonpath.herokuapp.com/ to try and find the right JSONPATH syntax to filter out anything not starting with foo, and having id == 123.
Getting it to filter the ones that do start with foo is easy:
$..[?(#.name =~ /foo.*/i)]
This yields the following results:
[
{
"name" : "foobar",
"id" : 123
},
{
"name" : "foobar",
"id" : 456
}
]
I can get rid of the id 456 by adding an additional filter like so:
$..[?(#.name =~ /foo.*/i && #.id==123)]
But how do I do the opposite of getting the name starting with foo? I want all entities that do not start with foo.
I tried something like this:
$..[?(!#.name =~ /foo.*/i && #.id==123)]
Which at least parses as valid JSONPATH, and should negate the filter, but for some reason it still happily only reports the foobar entry:
[
{
"name" : "foobar",
"id" : 123
}
]
How can I achieve a NOT LIKE in JSONPATH?
Thanks!
Regex to identify data not starting with a given string foo:
^([^f]|f[^o]|fo[^o])
If your regex engine supports negative lookahead, that reduces to
^(?!foo)
Note the starting anchor (^) that limits the permissible matching location to the start of the test string.
Your attempt $..[?(!#.name =~ /foo.*/i && #.id==123)] is almost correct. Surround the regex condition with parenthesis before negating with ! like so $..[?(!(#.name =~ /foo.*/i) && #.id==123)]. Tested at https://jsonpath.herokuapp.com/
Edit: This was assuming that you were using Jayway's jsonpath (Java, https://github.com/json-path/JsonPath), but from the documentation link you provided for SmartBear, it looks like it uses the Goessner jsonpath (Javascript, https://goessner.net/articles/JsonPath/). Both, for whatever reason use slightly differing syntaxes.
Thanks to #collapsar for nudging me in the correct direction, in that the key to solving it was in the regular expression (but specifically using the JavaScript Regular Expression syntax, and merging that with the JSONPath syntax).
What actually ended up doing the trick was reading the documentation for JASONPath a bit more careful. It states:
=~
Match a JavaScript regular expression. For example, [?(#.description =~ /cat.*/i)] matches items whose description starts with cat (case-insensitive).
Note: Not supported at locations that use Ready! API 1.1.
The link to Javascript Regular Expression in turn contains the following:
[^xyz]
A negated or complemented character set. That is, it matches anything that is not enclosed in the brackets. You can specify a range of characters by using a hyphen. Everything that works in the normal character set also works here.
For example, [^abc] is the same as [^a-c]. They initially match 'r' in "brisket" and 'h' in "chop."
The resulting expression that works is:
$..[?(#.name =~ /[^foo].*/ && #.id == 123)]
Result:
[
{
"name" : "bar",
"id" : 123
}
]
(I added an additional bar with id 456 to the JSON payload I had, to double-check the filter also worked).