jq: populate object bedides of field exists - jq

I'm using this filter in:
map({
name,
deceased: (.deceased? // empty | split(" ") | first | strptime("%Y/%m/%d") | strftime("%F"))
})
input objects are:
{
"name": "name1",
"birth": "1950/05/05",
"deceased": "1982/05/19"
}
{
"name": "name2",
"birth": "1982/05/19"
}
I'm getting:
[
{
"name": "name1",
"deceased": "1982-05-19"
}
]
I need to get:
[
{
"name": "name1",
"deceased": "1982-05-19"
},
{
"name": "name2"
}
]
Any ideas?

You can test for existence using has
map(
{name} +
if has("deceased")
then {deceased: (.deceased | strptime("%Y/%m/%d") | strftime("%F"))}
else {}
end
)
[
{
"name": "name1",
"deceased": "1982-05-19"
},
{
"name": "name2"
}
]
Demo
If .deceased can only exist as string, then this would be equivalent:
map(
{name, deceased}
| .deceased |= (strings | strptime("%Y/%m/%d") | strftime("%F"))
)
[
{
"name": "name1",
"deceased": "1982-05-19"
},
{
"name": "name2"
}
]
Demo

Related

JQ does not contain for "key": "value" JSON structure

The question is similar to THIS, but the JSON structure is different.
In my usecase JSON has an arrays with key:value data:
{
"data":[
{
"name":"banana",
"tags":[
{
"id":"yellow"
},
{
"id":"long"
}
]
},
{
"name":"apple",
"tags":[
{
"id":"red"
},
{
"id":"round"
}
]
},
{
"name":"orange",
"tags":[
{
"id":"orange"
},
{
"id":"round"
},
{
"id":"colored"
}
]
}
]
}
What required is to filter the only elements that do not have certain keyword - "red" for instance.
When i use jq '.data[] | select(.tags[].id | index( "red" ))' it brings me the correct resut of 'apple' (as it has "id": "red") and 'orange' (as it has "id": "colored").
However, when i add the negation jq '.data[] | select(.tags[].id | index( "red" ) | not)' the results are more than strange, with elements' duplication, totally enigmatic.
How can i use jq to filter the result the way it returns only elements that do not have the exact match among the array values?
You can use all to give a condition all items must meet. Here, all values in .tags[].id must be unequal != to "red":
jq '.data[] | select(all(.tags[].id; . != "red"))'
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
Add .name and use the -r option to only get the names:
jq -r '.data[] | select(all(.tags[].id; . != "red")).name'
banana
orange
Demo
index works on an array, your current filter does not pass index to an array, therefore you're getting other results then expected.
.data[] | select([ .tags[].id ] | index("red") | not)
Here we create an array with all the id's [ .tags[].id ] and use that array to check for red: | index("red") | not
The above filter gives the following output:
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
If you want to exclude 'colored', use contains():
.data[] | select([ .tags[].id ] | contains(["red"]) | not)
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
Demo

Conditional append a element to an array

Here is the jq I have, it just wants to build a new element and then append it to an array,
[.[] | . as { foo: $foo1, bar: $bar1} |
{
names: ([
$foo1 | range(0;length) as $i |
{ key: ($foo1[$i]) }
] + [{ key: $bar1 }])
} |
{
values: .names,
}
]
And suppose I have a json like this,
{
"foo":[
"key1",
"key2"
],
"bar": "key3"
}
This will generate a json file like this,
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
},
{
"key": "key3"
}
]
}
]
But the element should be only appended when the $bar is not an empty string, can I do something like this?
[.[] | . as { foo1: $foo1, bar1: $bar1 if $bar != ""}
...
or do it when is appended,
names: ([
$foo1 | range(0;length) as $i |
{ key: ($foo1[$i]) }
] + [{ key: $bar1 }] | if $bar != "")
thanks in advance for any help!
You could just use select to filter out that case
jq '[{values: (.foo + [.bar | select(. != "")]) | map({key:.})}]'
If .bar == "key3", it prints
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
},
{
"key": "key3"
}
]
}
]
If .bar == "", it prints
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
}
]
}
]
If .bar does not exist, it will print
[
{
"values": [
{
"key": "key1"
},
{
"key": "key2"
},
{
"key": null
}
]
}
]
If in this last case you want to have the same result as with .bar == "", then change . != "" to values != "" in the filter to consider only values that are not null (or to strings != "" to only consider (non-empty) strings and disregard any other type).

Parsing on jq with_entries

I have the following data
Command output:
| jq '.rrsets[]'
{
"comments": [],
"name": "name1.",
"records": [
{
"content": "10.10.10.10",
"disabled": false
}
],
"ttl": 60,
"type": "A"
}
{
"comments": [],
"name": "name2.",
"records": [
{
"content": "20.20.20.20",
"disabled": false
}
],
"ttl": 60,
"type": "CNAME"
}
I want to get names where type is A.
Help, tell me how to do this?
| jq '.rrsets[] | with_entries(select(.key== "name", .value == "A"))'
{
"name": "name1."
}
{
"name": "name2.",
"type": "A"
}
Displays all the lines, but I only need where type = A
I'm not sure if this is what you are looking for, but wouldn't simply ... | select(.type == "A") do the trick?
... | jq '.rrsets[] | select(.type == "A")'
{
"comments": [],
"name": "name1.",
"records": [
{
"content": "10.10.10.10",
"disabled": false
}
],
"ttl": 60,
"type": "A"
}
Demo
And then just get the .name if you want only that (using -r to get rid of the JSON formatting):
... | jq -r '.rrsets[] | select(.type == "A").name'
name1.
Demo

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

`jq` selection based on condition of different levels

I'd like to select the following node in a long json file.
The conditions are "locator": "mmc1" and "#name": "section-title". This nested structure makes the jq query very complex. Notice that I have to specify things like "locator" and "section-title" multiple times.
.. | .["$$"]?
| select((.[]? | ."#name" == "section-title"?) and (..[]? | .locator? | test("mmc[0-9]+")?))
| [
(..[]? | select(.locator? | test("mmc[0-9]+")?) | .locator)
, (.[] | select(."#name" == "section-title") | ._)
] | #tsv
Could anybody let me know how to make the query simpler in jq yet make sure its function is exact the same as the original query? Just making the following simplified test input producing the same output should not be considered as an equivalent query. Thanks.
...
"$$": [
{
"#name": "label",
"_": "Appendix A"
},
{
"#name": "section-title",
"$": {
"id": "sectitle0145"
},
"_": "Supplementary data"
},
{
"#name": "para",
"$": {
"id": "p0210",
"view": "all"
},
"$$": [
{
"#name": "__text__",
"_": "The following is the supplementary data related to this article:"
},
{
"#name": "display",
"$$": [
{
"#name": "e-component",
"$": {
"id": "ec1"
},
"$$": [
{
"#name": "link",
"$": {
"locator": "mmc1",
"type": "simple",
"role": "http://data.elsevier.com/vocabulary/ElsevierContentTypes/46.1",
"href": "pii:S2212877817302818/mmc1",
"id": "aep-link-id8"
}
}
]
}
]
}
]
}
]
...
See the output below.
$ jq '.. | .["$$"]? | select((.[]? | ."#name" == "section-title"?) and (..[]? | .locator? | test("mmc[0-9]+")?)) | [ (..[]? | select(.locator? | test("mmc[0-9]+")?) | .locator) , (.[] | select(."#name" == "section-title") | ._) ] | #tsv' < 1.json
"mmc1\tSupplementary data"
$ cat 1.json
{
"$$": [
{
"#name": "label",
"_": "Appendix A"
},
{
"#name": "section-title",
"$": {
"id": "sectitle0145"
},
"_": "Supplementary data"
},
{
"#name": "para",
"$": {
"id": "p0210",
"view": "all"
},
"$$": [
{
"#name": "__text__",
"_": "The following is the supplementary data related to this article:"
},
{
"#name": "display",
"$$": [
{
"#name": "e-component",
"$": {
"id": "ec1"
},
"$$": [
{
"#name": "link",
"$": {
"locator": "mmc1",
"type": "simple",
"role": "http://data.elsevier.com/vocabulary/ElsevierContentTypes/46.1",
"href": "pii:S2212877817302818/mmc1",
"id": "aep-link-id8"
}
}
]
}
]
}
]
}
]
}
If you want to get the "locator" value off of all objects unconditionally, you could use this:
..|objects.locator|strings
To find all objects with #name == "section-title" and select the _ value:
..|select(objects."#name" == "section-title")._
Putting it all together:
[(..|objects.locator|strings), (..|select(objects."#name" == "section-title")._)] | #tsv
https://jqplay.org/s/xHWg8aGSSS
The following is simpler in at least some respects, produces the desired result, and seems to reflect the requirements:
..
| .["$$"]?
| (.. | objects | .locator | strings | select(test("mmc[0-9]+")) ) as $locator
| (.. | objects | select(.["#name"] == "section-title") | ._) as $st
| [$locator, $st]
| #tsv

Resources