jq: where select clause is applied - jq

Here my documents:
[
{
"id":"f3b8c257-9950-45e7-9e79-ace19ec8905e",
"identifier":[
{
"system":{
"value":"urn:oid:2.16.724.4.9.10.2"
},
"value":{
"value":"10839812"
}
}
]
},
{
"id":"f0a1e3ae-826f-4a03-b29e-10ef3bc86ea0",
"identifier":null
}
]
Currently, I'm aplying this jq filter:
map(
{
id,
dni: .identifier[] | (select(.system.value == "urn:oid:1.3.6.1.4.1.19126.3") | .value.value)
}
)
However, I'm getting this message:
jq: error (at practitioner-mongoexport.json:146715): Cannot iterate over null (null)
As you can guess, problem arises processing second object where .identifier: null.
I've tried with that:
map(
{
id,
dni: select(.identifier) | .identifier[] | (select(.system.value == "urn:oid:1.3.6.1.4.1.19126.3") | .value.value)
}
)
I've also tried with:
map(
select(.identifier) |
{
id,
dni: .identifier[] | (select(.system.value == "urn:oid:1.3.6.1.4.1.19126.3") | .value.value)
}
)
Then result is [].
I don't quite figure out what I'm doing wrong.
My desired output would be:
[
{
"id":"f3b8c257-9950-45e7-9e79-ace19ec8905e",
"dni": "10839812"
}
]
Any ideas?

You need to deal with the case if .identifier is null. Here, the ? operator may help.
without selection
jq 'map({id, dni: (.identifier[]?).value.value})'
Demo
with selection
jq 'map({id, dni: (.identifier[]? | select(.system.value == "urn:oid:2.16.724.4.9.10.2")).value.value})'
Demo
[
{
"id": "f3b8c257-9950-45e7-9e79-ace19ec8905e",
"dni": "10839812"
}
]

map(
select(.identifier?[]?.system.value == "urn:oid:2.16.724.4.9.10.2")
| { id, "dni": .identifier[].value.value }
)
Will generate:
[
{
"id": "f3b8c257-9950-45e7-9e79-ace19ec8905e",
"dni": "10839812"
}
]
The trick here is .identifier?[]?.system.value where the ? will ignore .identifier if it's null
JqPlay Demo

Related

Cannot index array with string "xxx"

I have a json file as this,
# cat input.json
{
"foo":[
"key1",
"key2"
],
"bar": "key3"
}
And I want to join all those strings ("key1", "key2", key3") into one string, and I define the filter as this,
# cat filter.jq
[.[] | . as { foo: $names, bar: $name} | {
name1: [ $names | range(0;length) as $i | {
key1: ($names[$i])
}],
name2: {
key2: $name
}
} | {
values: (.name1 | map(.key1) | join(" ") + .name2.key2)
}]
But this doesn't work,
# <input.json jq --slurp --from-file filter.jq > output.json
jq: error (at <stdin>:8): Cannot index array with string "name2"
What's wrong going here?
btw. concat with a string seems works.
values: (.name1 | map(.key1) | join(" ") + "key3")
The problem is underspecified, but you might wish to consider:
[.. | strings] | join(" ")
or similar.

jq: pick value if field exist

This is my script:
def pick_partOf($uids):
(reduce $uids[] as $uid (
{};
. + ($uid | {(.oid1): .id})
)) as $dict
| map(. + { partOf: $dict[."parent-identifier-value"] } | del(..|nulls));
def pick_organization:
{
resourceType: "Organization",
active: true,
partOf: .partOf?
};
pick_partOf($identifiers) | pick_organization
Problem here, is that sometimes some input objects doesn't contains ."parent-identifier-value", and I'm getting:
jq: error (at rsan.json:7): Cannot index object with null
Those are my input objects:
{
"identifier-value": "61",
"name": "name61"
}
{
"id": "62",
"name": "name62",
"parent-identifier-value": "61"
}
Currently, I'm launching jq using empty array as $identifiers arg:
jq -s -f build-organization-bundle.jq --argjson identifiers '[]' rsan.json
However, my identifiers is an other json files like this:
{
"id": "be8a02b6-54b9-450f-bf45-f28ab7ebf2dd",
"oid1": "61"
}
{
"id": "f1652f3a-bca9-433c-a2c4-c924811196f6",
"oid1": "62"
}
I've tried with:
def pick_partOf($uids):
(reduce $uids[] as $uid (
{};
. + ($uid | {(.oid1): .id})
)) as $dict
| map(. + (
if (.|has("parent-identifier-value")) then
{ partOf: $dict[."parent-identifier-value"] }
else null end
))
| del(..|nulls));
But here I'm facing with some syntax issue I don't quite figure out how to fix it.
You did not specify why you want pick_partOf to achieve.
If its output should be
{
"identifier-value": "61",
"name": "name61"
}
{
"id": "62",
"name": "name62",
"parent-identifier-value": "61",
"partOf": "be8a02b6-54b9-450f-bf45-f28ab7ebf2dd"
}
then you can use
if ."parent-identifier-value" then
.partOf = $dict[."parent-identifier-value"]
else
.
end
Demo on jqplay
Repeating ."parent-identifier-value" was annoying me.
."parent-identifier-value" as $piv |
if $piv then .partOf = $dict[$piv] else . end
Demo on jqplay

JQ: How do I extract item which has part of subitem value of "FOO" [duplicate]

I have a JSON file that looks like this:
{
"InstanceId": "i-9KwoRGF6jbhYdZi823aE4qN",
"Tags": [
{
"Key": "blah",
"Value": "server-blah"
},
{
"Key": "environment",
"Value": "ops"
},
{
"Key": "server_role",
"Value": "appserver"
},
{
"Key": "Name",
"Value": "some_name"
},
{
"Key": "product",
"Value": "some_server"
}
]
}
{
...more objects like the above...
}
I need to display the InstanceId where "Key" == "environment" and "Value" == "ops".
I have jq-1.6.
If I say:
cat source.json | jq '
{ InstanceId, Tags } |
(.Tags[] | select( .Key == "environment" ))
'
I get some of what I want, but I cannot figure out how to include InstanceId in the output nor how to incorporate the "and" part of the select.
Here is a simple but efficient approach using any:
select( any(.Tags[]; .Key=="environment" and .Value == "ops") )
| .InstanceId
An alternative approach that avoids .Tags[]:
{"Key": "environment", "Value": "ops"} as $object
| select( .Tags | index($object) )
| .InstanceId
I'm not sure if this is the exact output you're looking for (comment if it isn't), but this will output the InstanceIds of JSON objects that contain a Tag with Key environment and Value ops.
jq 'select( .Tags[] | (.Key == "environment" and .Value == "ops")) | .InstanceId' < source.json

Accessing field with jq that can be string or array

I have a large dump of data in json that looks like:
[{
"recordList" : {
"record" : [{
"Production" : {
"creator" : {
"name" : "A"
}
}
},
{
"Production" : {}
},
{
"Production" : [{
"creator" : {
"name" : "B"
},
"creator" : {
"name" : "C"
}
}]
}]
}
}]
I need to check if there is at least one creator in a record or not. If there is I give a 1 else a 0 for that field in a CSV-file.
My code:
jq -r '.[].recordList.record[]|"\(if ((.Production.creator.name)? // (.Production[]?.creator.name)?) == null or ((.Production.creator.name)?|length // (.Production[]?.creator.name)?|length) == 0 then 0 else 1 end),"' file.json
The problem is that the field 'Production' is only an array when there are multiple creators.
The result I want to get in this case is:
1,
0,
1,
jq solution:
jq -r '.[].recordList.record[].Production
| "\(if ((type == "array" and .[0].creator.name !="")
or (type == "object" and .creator.name and .creator.name !=""))
then 1 else 0 end),"' file.json
The output:
1,
0,
1,
Simplified jq solution:
jq -r '.[].recordList.record[].Production
| ((type == "array" and .[0].creator.name) or .creator.name)
| if . then "1," else "0," end' file.json

Interaction between {} and select

Here's my test data:
[
{
"id": "id-1",
"tags": {
"key": "name",
"value": "name-1"
}
},
{
"id": "id-2"
}
]
I'm trying to simplify the output, to show the 'name' field if present, and always show the id. For example, this script almost works:
~ $ cat testdata | jq '.[] | {id, name: .tags.value}'
{
"id": "id-1",
"name": "name-1"
}
{
"id": "id-2",
"name": null
}
When I try to add in a guard against .keys not existing and filter for the section of 'keys' I care about, here's what happens:
~ $ cat testdata | jq '.[] | {id, name: (select(.tags.key == "name") | .tags.value)}'
{
"id": "id-1",
"name": "name-1"
}
I assume {} is somehow ending up with a zero-length array instead of 'null'. What should I be using instead of |? What am I misunderstanding?
I ended up solving the problem using: [POSSIBLY_MATCHED_EXPRESSION][0], in this case:
cat testdata | jq '.[] | {id, name: ([select(.tags.key == "name") | .tags.value][0])}'
If I'm understanding correctly, if you wanted to include a name only if it existed, I'd do this:
map({id} + with_entries(select(.key == "tags") | .value))
Otherwise if you don't mind null names:
map({id, name: with_entries(select(.key == "tags") | .value) | .name})
Here's a more general solution if you have other "tags" so it's not hardcoded to only accept name values.
This assumes that any object value is actually a key/value pair.
map(with_entries(if .value | type == "object" then .value else . end))
Or if tags is the only dynamic property:
map(with_entries(if .key == "tags" then .value else . end))
If the goal is to produce:
{"id":"id-1","name":"name-1"}
{"id":"id-2"}
then the following three expressions are essentially equivalent solutions:
.[] | if .tags.key == "name" then {id, name: .tags.value} else {id} end
.[] | {id} + (if .tags.key == "name" then {name: .tags.value} else {} end)
.[] | (select(.tags.key == "name") | {id, name: .tags.value}) // {id}
You could just add
| if .name == null then del(.name) else . end
to the end of your filter to get rid of the .name key when its value is null.
With your test data, the following
.[]
| {id, name:.tags.value }
| if .name == null then del(.name) else . end
produces
{
"id": "id-1",
"name": "name-1"
}
{
"id": "id-2"
}

Resources