jq query with condition and format output/labels - jq

I have a JSON file:
[
{
"platform": "p1",
"id": "5",
"pri": "0",
"sec": "20"
}
]
[
{
"platform": "p2",
"id": "6",
"pri": "10",
"sec": "0"
}
]
I can to format it to the form:
$ jq -c '.[]|{PLATFORM: .platform, ID: .id, PRI: .pri, SEC: .sec}' test.json
{"PLATFORM":"p1","ID":"5","PRI":"0","SEC":"20"}
{"PLATFORM":"p2","ID":"6","PRI":"10","SEC":"0"}
$
but how to ignore SEC/PRI with "0" and get output in form:
PLATFORM:p1, ID:5, SEC:20
PLATFORM:p2, ID:6, PRI:10
I can process it with bash/awk command, but maybe someone have a solution with jq directly.
thank you,

You can use conditional statements to remove the unwanted keys, e.g.:
if (.sec == "0") then del(.sec) else . end
The formatting could be done with #tsv by converting the data to an array, e.g.:
filter.jq
.[] |
if (.sec == "0") then del(.sec) else . end |
if (.pri == "0") then del(.pri) else . end |
to_entries |
map("\(.key | ascii_upcase):\(.value)") |
#tsv
Run it like this:
jq -crf filter.jq test.json
Output:
PLATFORM:p1 ID:5 SEC:20
PLATFORM:p2 ID:6 PRI:10

jq solution:
jq -c 'def del_empty($k): if (.[$k]|tonumber > 0) then . else del(.[$k]) end;
.[] | {PLATFORM: .platform, ID: .id, PRI: .pri, SEC: .sec}
| del_empty("PRI")
| del_empty("SEC")' test.json
The output:
{"PLATFORM":"p1","ID":"5","SEC":"20"}
{"PLATFORM":"p2","ID":"6","PRI":"10"}

Related

Conditionally output a field?

In this example I only want isGreaterThanOne field to be shown if it's true. Here's what I started with (always shown)
echo '[{"a":5},{"a":1}]' | jq '[.[] | {value:.a, isGreaterThanOne:(.a>1)}]'
I inserted an if statement
echo '[{"a":5},{"a":1}]' | jq '[.[] | {value:.a, X:(if .a>1 then "Y" else "N" end) }]'
Then got stuck trying to move the field into the conditional. Also it seems like I must have an else with an if
echo '[{"a":5},{"a":1}]' | jq '[.[] | {value:.a, (if .a>1 then (K:"Y)" else (L:"N") end) }]'
I want the below as the result (doesn't need to be pretty printed)
[
{
"value": 5,
"X": "Y"
},
{
"value": 1,
}
]
Using if, make one branch provide an empty object {} which wouldn't contain the extra field:
map({value: .a} + if .a > 1 then {X: "Y"} else {} end)
Demo
Alternatively, equip only selected items with the extra field:
map({value: .a} | select(.value > 1).X = "Y")
Demo
Output:
[
{
"value": 5,
"X": "Y"
},
{
"value": 1
}
]

Matching When Value Is List

I have an API dump file that is similar to this.
{
"abc": {
"Code": "ABC",
"Type": [] },
"def": {
"Code": "DEF",
"Type": [
"A"
]
},
"ghi": {
"Code": "GHI",
"Type": [
"B"
]
},
"jkl": {
"Code": "JKL",
"Type": [
"A",
"B"
]
},
"mno": {
"Code": "MNO",
"Type": [ "Universal" ]
}
}
I am trying to extract objects, and get Code keys based on certain Type matches.
For example.
Trying to where Type matches "A", "B", "A, B", or Universal, I am getting objects that contain both, or nothing in certain cases.
Here is what I tried.
jq -r '.[] | select(.Type[] == "A") | .Code' /tmp/test.json
I get
DEF JKL
Which is unexpectedly matching Type ["A"} and Type ["A", "B"]
jq -r '.[] | select(.Type[] == "B") | .Code' /tmp/test.json
I get.
GHI JKL
Which is unexpectedly matching Type ["B"} and Type ["A", "B"]
jq -r '.[] | select(.Type[] == "A, B") | .Code' /tmp/test.json
Matches nothing.
This works as expected.
jq -r '.[] | select(.Type[] == "Universal") | .Code' /tmp/test.json
MNO
If you wanted exact match, save following into script.jq :
.[] | select(.Type == $match) | .Code
Then test with
$ jq -r --argjson match '["A"]' -f script.jq test.json
DEF
$ jq -r --argjson match '["B"]' -f script.jq test.json
GHI
$ jq -r --argjson match '["A", "B"]' -f script.jq test.json
JKL
The jq program
.[] | select(any(.Type[]; IN("A","B","A, B","Universal"))) | .Code
produces:
"DEF"
"GHI"
"JKL"
"MNO"

Extract nested properties from an array of objects

I have the following JSON file :
{
"filter": [
{
"id": "id_1",
"criteria": {
"from": "mail#domain1.com",
"subject": "subject_1"
},
"action": {
"addLabelIds": [
"Label_id_1"
],
"removeLabelIds": [
"INBOX",
"SPAM"
]
}
},
{
"id": "id_2",
"criteria": {
"from": "mail#domain2.com",
"subject": "subject_1"
},
"action": {
"addLabelIds": [
"Label_id_2"
],
"removeLabelIds": [
"INBOX",
"SPAM"
]
}
}
]
}
And I would like to extract emails values : mail#domain1.com and mail#domain2.com
I have tried this command:
jq --raw-output '.filter[] | select(.criteria.from | test("mail"; "i")) | .id'
But does not work, I get this error :
jq: error (at <stdin>:1206): null (null) cannot be matched, as it is
not a string exit status 5
Another point : how to display the value of "id" key, where "from" key value = mail#domain1.com ?
So in my file id = id_1
Do you have an idea ?
If you only need to extract the emails from .criteria.from then this filter is enough as far as I can tell:
jq --raw-output '.filter[].criteria.from' file.json
If some objects don't have a criteria object then you can filter out nulls with:
jq --raw-output '.filter[].criteria.from | select(. != null)' file.json
If you want to keep the emails equal to "mail#domain1.com":
jq --raw-output '.filter[].criteria.from | select(. == "mail#domain1.com")' file.json
If you want to keep the emails that start with "mail#":
jq --raw-output '.filter[].criteria.from | select(. != null) | select(startswith("mail#"))' file.json
I would like to extract emails values
There is a wide spectrum of possible answers, with these
amongst the least specific with respect to where in the JSON the email addresses occur:
.. | objects | .from | select(type=="string")
.. | strings | select(test("#([a-z0-9]+[.])+[a-z]+$"))

Jq getting output from nested hash

Hi I am trying to parse below hash with jq
{
"name": "a",
"data": [
{
"sensitive": false,
"type": "string",
"value": "mykeypair"
},
{
"sensitive": false,
"type": "int",
"value": 123
}
]
}
and get output like
a,string,mykeypair
a,int,123
I am able to get output like this
a,string,mykeypair
a,int,mykeypair
a,string,123
a,int,123
jq solution:
jq -r '.name as $n | .data[] | [$n, .type, .value] | #csv' file.json
The output:
"a","string","mykeypair"
"a","int",123
If it's mandatory to output unquoted values:
jq -r '.name as $n | .data[] | [$n, .type, "\(.value)"] | join(",")' file.json
The output:
a,string,mykeypair
a,int,123

How do i add an index in jq

I want to use jq map my input
["a", "b"]
to output
[{name: "a", index: 0}, {name: "b", index: 1}]
I got as far as
0 as $i | def incr: $i = $i + 1; [.[] | {name:., index:incr}]'
which outputs:
[
{
"name": "a",
"index": 1
},
{
"name": "b",
"index": 1
}
]
But I'm missing something.
Any ideas?
It's easier than you think.
to_entries | map({name:.value, index:.key})
to_entries takes an object and returns an array of key/value pairs. In the case of arrays, it effectively makes index/value pairs. You could map those pairs to the items you wanted.
A more "hands-on" approach is to use reduce:
["a", "b"] | . as $in | reduce range(0;length) as $i ([]; . + [{"name": $in[$i], "index": $i}])
Here are a few more ways. Assuming input.json contains your data
["a", "b"]
and you invoke jq as
jq -M -c -f filter.jq input.json
then any of the following filter.jq filters will generate
{"name":"a","index":0}
{"name":"b","index":1}
1) using keys and foreach
foreach keys[] as $k (.;.;[$k,.[$k]])
| {name:.[1], index:.[0]}
EDIT: I now realize a filter of the form foreach E as $X (.; .; R) can almost always be rewritten as E as $X | R so the above is really just
keys[] as $k
| [$k, .[$k]]
| {name:.[1], index:.[0]}
which can be simplified to
keys[] as $k
| {name:.[$k], index:$k}
2) using keys and transpose
[keys, .]
| transpose[]
| {name:.[1], index:.[0]}
3) using a function
def enumerate:
def _enum(i):
if length<1
then empty
else [i, .[0]], (.[1:] | _enum(i+1))
end
;
_enum(0)
;
enumerate
| {name:.[1], index:.[0]}

Resources