jq: get first n elements of an array field - jq

I'm getting some issues trying to get only firsts 3 elements of an array:
json is like this:
{
"resourceType":"Practitioner",
"id":"328001",
"meta":{
"versionId":"1",
"lastUpdated":"2021-11-08T12:47:51.239+01:00",
"source":"#BUkX5uagwf3PcHzd"
},
"identifier":[
{
"use":"official",
"system":"urn:oid:1.3.6.1.4.1.19126.3",
"value":"00396185X"
},
{
"use":"official",
"system":"urn:oid:2.16.724.4.9.10.2",
"value":"0831515"
},
{
"use":"official",
"system":"urn:oid:2.16.724.4.9.10.2",
"value":"0831515"
},
{
"use":"official",
"system":"urn:oid:2.16.724.4.9.10.2",
"value":"0831515"
}
],
"active":true,
"name":[
{
"use":"official",
"text":"JULIAN RODRIGUEZ LARREA",
"family":"RODRIGUEZ",
"_family":{
"extension":[
{
"url":"http://hl7.org/fhir/StructureDefinition/humanname-mothers-family",
"valueString":"LARREA"
}
]
},
"given":[
"JULIAN"
]
}
],
"qualification":[
{
"code":{
"coding":[
{
"system":"urn:oid:2.16.840.1.113883.6.96",
"code":"62247001"
}
]
}
}
]
}
Is there any way to get only first 3 elements od .identifier?
jq -r '(.identifier[] | .system, .value)'
Any ideas?

If you are looking to print only those specific fields on the first 3 results, use the slice operator. See jqplay for demo
.identifier[:3] | map({system, value})

Related

Can jq select via a filter while retaining the full input context?

Is there a generic mechanism within jq to select arbitrary elements of a JSON file but return the full structural context of those elements? For example, if I have the following:
{
"foo": {
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
},
"bar": [
1,
4,
5
],
"baz": true
}
using the filter .foo,.baz would normally result in:
{
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
}
true
but what I'd like is to get:
{
"foo": {
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
},
"baz": true
}
I can solve that specifically for the given filter using select, but I'd like something generic, to be able to run the same code with a different filter and get the same type of result, e.g. running with the filter .foo.three,.bar[1] would result in:
{
"foo": {
"three": {
"hello": "world"
}
},
"bar": [
4
]
}
Thanks!
This would give exactly the result you wanted for .foo.three,.bar[1] :
jq 'def extract(f):
. as $input |
reduce path(f) as $path (
null;
if ($path | last | type) == "string"
then setpath($path; $input | getpath($path))
else setpath(($path|.[:-1]);
getpath($path|.[:-1]) +
[$input | getpath($path)]
)
end
);
extract(.foo.three, .bar[1])' data.json
At best you could do an object construction directly by naming the key names under {..} and apply a further transformation to get only the desired paths
{foo, bar} | .foo |= {three} | .bar |= [.[1]]
jqplay demo
You could convert your queries into paths, and the input into a stream, select the pieces matching the query path, and rebuild it to a single output:
def extract(f):
reduce (
path(f) as $path | tostream
| select(length > 1 and (.[0] | index($path) == 0))
) as $set (
null;
setpath($set[0]; $set[1])
);
First example using .foo and .baz:
jq 'def extract(f): …; extract(.foo, .baz)'
{
"foo": {
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
},
"baz": true
}
Demo
As with sparse arrays though, it'll fill up the missing items with null, as otherwise the index wouldn't match anymore. Second example using .foo.three and .bar[1]:
jq 'def extract(f): …; extract(.foo.three, .bar[1])'
{
"foo": {
"three": {
"hello": "world"
}
},
"bar": [
null,
4
]
}
Demo

jq: reduce and map combination

Here my current jq script:
def reduce_generateId:
.[] | (. + {generalPractitionerCode: (.UAB_UP + "-" + .UAB_COD_UAB)});
def reduce_generalPractitioner($practitionerRole):
(reduce $practitionerRole[] as $g (
{};
.[$g.oid1 + "-" + $g.oid2].generalPractitioner += ($g | [.id])
)) as $dict
| $dict;
reduce_generateId | reduce_generalPractitioner($generalPractitioner)
My jq command is:
jq -s -f merge-patient.jq --argfile generalPractitioner reduced_ids.json reduced_pacient.json
Where reduced_pacient.json:
{ "UAB_UP": "00003", "UAB_COD_UAB": "3212", "INVENTAT": "02"}
{ "UAB_UP": "00006", "UAB_COD_UAB": "5881", "INVENTAT": "102"}
{ "UAB_UP": "00006", "UAB_COD_UAB": "5751", "INVENTAT": "102"}
and reduced_ids.json:
[
{
"id": "3e67b455-8cdb-4bc0-a5e1-f90253870fc9",
"oid1": "04374",
"oid2": "INFP3"
},
{
"id": "0f22e5ff-70bc-457f-bdaf-7afe86d478de",
"oid1": "04376",
"oid2": "INF07"
}
]
As you can see, redux_generalPractitioner returns $dict straightforwardly. I'm getting this:
{
"04374-INFP3": {
"generalPractitioner": [
"3e67b455-8cdb-4bc0-a5e1-f90253870fc9"
]
},
"04376-INF07": {
"generalPractitioner": [
"0f22e5ff-70bc-457f-bdaf-7afe86d478de"
]
}
}
{
"04374-INFP3": {
"generalPractitioner": [
"3e67b455-8cdb-4bc0-a5e1-f90253870fc9"
]
},
"04376-INF07": {
"generalPractitioner": [
"0f22e5ff-70bc-457f-bdaf-7afe86d478de"
]
}
}
{
"04374-INFP3": {
"generalPractitioner": [
"3e67b455-8cdb-4bc0-a5e1-f90253870fc9"
]
},
"04376-INF07": {
"generalPractitioner": [
"0f22e5ff-70bc-457f-bdaf-7afe86d478de"
]
}
}
As you can see, I'm getting $dict three times.
I don't quite figure out why it's generating three $dict instead of one.
If I only perform reduce_generalPractitioner($generalPractitioner), I'm getting:
{
"04374-INFP3": {
"generalPractitioner": [
"3e67b455-8cdb-4bc0-a5e1-f90253870fc9"
]
},
"04376-INF07": {
"generalPractitioner": [
"0f22e5ff-70bc-457f-bdaf-7afe86d478de"
]
}
}
Any ideas?
Your program input, reduced_pacient.json is a stream of three objects. As you have set the --slurp flag, the initial context of your script is an array of three elements.
First, you're calling reduce_generateId which by iteration .[] decomposes the array into its three items. Thus reduce_generateId has three outputs.
Next, these are fed by pipe into your reduce_generalPractitioner function which consequently is run three times. Each run produces your $dict once, yielding three times in total.
Remember: Although your function reduce_generalPractitioner has an input parameter, it also has (just like every filter in jq) a general input context (addressed through piping), which determines the value of . and subsequently the number of runs.

Accessing ancestors of array with given value

I can't seem to find the secret sauce to make jq do what I want.
Given the following contrived input:
{
"node1": {
"1": {
"Aliases": ["one", "uno"]
},
"2": {
"Aliases": ["two", "dos"]
}
},
"node2": {
"a": {
"Aliases": ["alpha"]
},
"b": {
"Aliases": ["bravo"]
}
}
}
I want to return the keys of the ancestors of Aliases when Aliases contains a particular value.
For example, given the search key dos, I want to return node1 and 2.
You can play with this data in jqplay. Any help would be appreciated.
(paths | select(.[-2] == "Aliases")) as $p
| select( "dos" == getpath($p))
| $p[:-2][]
Notice that there's no need for an additional variable (. as $in).

How can I count number of elements after query?

I have a query:
.modules[].resources | select (.[]!=null)
and after it I have got:
{ somestuff } { somestuff } { somestuff }
when I add legth after all:
.modules[].resources | select (.[]!=null) | length
I have got:
1 1 1
but I need to count elements, so I need 3 in an output. How can I implement it ?
In fact it would be very useful to create an array from the first query output to operate with it furthure
[ { somestuff } , { somestuff } , { somestuff } ]
You can put the results of the query into a list and get the length of this list:
[ .modules[].resources | select (.[]!=null) ] | length
Since you indicated it would be very useful to create an array from the first query output, what you probably want to use here is map.
Let's assume your data is something like
the data from this question: jq: search by value from another array element
{
"modules": [
{
"resources": [
{
"type": "openstack_compute_instance_v2",
"primary": {
"id": "5edfe2bf-94df-49d5-8118-3e91fb52946b",
"attributes": {
"name": "jumpbox"
}
}
},
{
"type": "openstack_compute_floatingip_associate_v2",
"primary": {
"attributes": {
"instance_id": "5edfe2bf-94df-49d5-8118-3e91fb52946b",
"floating_ip": "10.120.241.21"
}
}
}
]
}
]
}
with this data your filter
.modules[].resources
| select (.[]!=null) #< do you really want this `[]` ?
would produce two copies of the entire .resources array. What you may want instead is
.modules[].resources
| map(select(.!=null))
which will give you an array
[
{
"type": "openstack_compute_instance_v2",
"primary": {
"id": "5edfe2bf-94df-49d5-8118-3e91fb52946b",
"attributes": {
"name": "jumpbox"
}
}
},
{
"type": "openstack_compute_floatingip_associate_v2",
"primary": {
"attributes": {
"instance_id": "5edfe2bf-94df-49d5-8118-3e91fb52946b",
"floating_ip": "10.120.241.21"
}
}
}
]
to get the length, just add length:
.modules[].resources
| map(select(.!=null))
| length
in this example giving
2
Because map(f) is defined as [ .[] | f ] the above filter is really
.modules[].resources
| [
.[]
| select(.!=null)
]
| length
In this form you can clearly see the construction of the intermediate array.
Note also that jq provides a built-in values filter which is defined as select(.!=null) so this example could be further simplified to just
.modules[].resources
| map(values)
| length
As mentioned at jq: count nest object values which satisfy condition
the best solution (e.g. because it does not involve construction of an intermediate array) would be to use count, defined as follows:
def count(s): reduce s as $i (0; .+1);
With this definition, you should be able simply to wrap count(...) around your query since it produces a stream:
count(.modules[].resources | select (.[]!=null))
Or maybe you want something closer to:
count(.modules[].resources | objects)

jq filter array of object unique property values

Very simple but beginning with jq.
What I have is an array of object. I want to have an array of object filtered by unique value 'myprop'
[
{
"myProp": "similarValue"
},
{
"myProp": "similarValue"
},
{
"myProp": "OtherValue"
}
]
Result I want:
[
{
"myProp": "similarValue"
},
{
"myProp": "OtherValue"
}
]
What I've tried:
.someContainerProp | unique[] .myProp
The problem is that is returns just the list of values not list of object
It was pretty easy actually
.values | unique_by(.myProp)

Resources