Join the 2 json report using jq - jq

I have 2 report files like the following
report1.json
{
"examples": [
{
"description": "desc 1",
"full_description": "full_description 1",
"status": "status 1",
"file_path": "file_path 1",
"run_time": 0.01
}
]
}
and report2.json
{
"examples": [
{
"description": "desc 2",
"full_description": "full_description 2",
"status": "status 2",
"file_path": "file_path 2",
"run_time": 0.02
}
]
}
I want to combine it using jq command with hope the end result is as follows
{
"examples": [
{
"description": "desc 1",
"full_description": "full_description 1",
"status": "status 1",
"file_path": "file_path 1",
"run_time": 0.01
},
{
"description": "desc 2",
"full_description": "full_description 2",
"status": "status 2",
"file_path": "file_path 2",
"run_time": 0.02
}
]
}
i have tried using this script jq -s '.[0] * .[1]' report1.json report2.json but it doesn't work, is there any solution to be able to combine according to my expected result using jq command ?

Try this:
jq -s ' [ (.[0] | keys[]) as $k | reduce .[] as $item (null; .[$k] += $item[$k]) ] | add' report1.json report2.json

Take the first input (automatically in context), and operarte on all subsequent objects using inputs (without the -n option). Then, simply add up all items you care about:
jq '.examples += [inputs.examples[]]' report*.json
{
"examples": [
{
"description": "desc 1",
"full_description": "full_description 1",
"status": "status 1",
"file_path": "file_path 1",
"run_time": 0.01
},
{
"description": "desc 2",
"full_description": "full_description 2",
"status": "status 2",
"file_path": "file_path 2",
"run_time": 0.02
}
]
}
Demo

If the point of the question is that you don't know the name of the top-level key, then perhaps the following is close to what you want:
jq -n '
[inputs | to_entries] | add | {(first.key): (map(.value) | add) }
' report{1,2}.json

Here's a very simple and straightforward solution using slurp (reads whole file into memory):
jq -s '{examples: map(.examples[])}' report1.json report2.json

Related

JQ query extraction

First off thanks to everyone who takes time to answer questions for us who are learning or struggling. I have this code that works as I need, I had help from knittl earlier.
jq -r '["Username", "Full name", "Superuser", "Active"],
> (.User[]
> | select(.is_superuser)
> | [ .username, .full_name, .is_superuser, .is_active ])
> | #csv' lbc.noprod1.xxx.json > lbc.noprod1.xxx_Superuser.csv
I have tried to adjust it so that I can use the same concept but to get other information and have this
jq -r '["Interface Name", "Vlan ID", "IP address", "Subnet Mask"],
(.ServiceEngine[].data_vnics[].vlan_interfaces[]
| [ .if_name, .vlan_id])
(.ServiceEngine[].data_vnics[].vlan_interfaces[].vnic_networks[].ip[].ip_addr[]
| [.addr])
(.ServiceEngine[].data_vnics[].vlan_interfaces[].vnic_networks[].ip[]
| [.mask])
| #csv' 20221004_lbc.prod1.xxx.json > 20221004_lbc.prod1.xxx_VNIC.csv
I receive the error below
jq: error: syntax error, unexpected '(', expecting $end (Unix shell quoting issues?) at , line 4:
(.ServiceEngine[].data_vnics[].vlan_interfaces[].vnic_networks[].ip[].ip_addr[]
jq: 1 compile error
If it helps this is an extract from the JSON with fields that aren't relevant removed but all formatting kept
"ServiceEngine": [
{
"data_vnics": [
{
"vlan_interfaces": [
{
"if_name": "bond1.109",
"vlan_id": 109,
"vnic_networks": [
{
"ctlr_alloc": false,
"ip": {
"ip_addr": {
"addr": "123.123.123.123",
"type": "V4"
},
"mask": 24
},
"mode": "STATIC"
}
],
"vrf_ref": "/api/vrfcontext/?tenant=admin&name=branch-pci-client-vlan109&cloud=Default-Cloud"
},
{
"dhcp_enabled": true,
"enabled": true,
"if_name": "bond1.1622",
"ip6_autocfg_enabled": true,
"is_mgmt": false,
"vlan_id": 1622,
"vnic_networks": [
{
"ctlr_alloc": false,
"ip": {
"ip_addr": {
"addr": "456.456.456.456",
"type": "V4"
},
"mask": 22
},
"mode": "STATIC"
}
],
"vrf_ref": "/api/vrfcontext/?tenant=admin&name=onprem-pci-prod-vlan1622&cloud=Default-Cloud"
},
As always if you have any tips and suggestions I would be greatful.
Kind regards
I think you are looking for this:
["Interface Name", "Vlan ID", "IP address", "Subnet Mask"],
(
.ServiceEngine[].data_vnics[].vlan_interfaces[]
| . as { $if_name, $vlan_id }
| .vnic_networks[].ip
| [ $if_name, $vlan_id, .ip_addr.addr, .mask]
)
| #csv
or
["Interface Name", "Vlan ID", "IP address", "Subnet Mask"],
(
.ServiceEngine[].data_vnics[].vlan_interfaces[]
| [ .if_name, .vlan_id ] + (.vnic_networks[].ip | [.ip_addr.addr, .mask])
)
| #csv
or
["Interface Name", "Vlan ID", "IP address", "Subnet Mask"],
(
.ServiceEngine[].data_vnics[].vlan_interfaces[]
| { if_name, vlan_id, ips: .vnic_networks[].ip }
| [ .if_name, .vlan_id, (.ips|.ip_addr.addr, .mask) ]
)
| #csv
All three generate the same CSV with one line per IP. Interface name and VLAN id will be printed multiple times if they contain more than one IP.

jq element matching with in a nested array

I have an array of array as following
[
{
"devices": [
{
"files": [
{
"id": "2",
"type": "file"
}
],
"path": "/tmp/file1"
},
{
"files": [
{
"id": "3",
"type": "file"
}
],
"path": "/tmp/file2"
}
],
"name": "a"
},
{
"devices": [
{
"files": [
{
"id": "4",
"type": "file"
}
],
"path": "/tmp/tfile"
},
{
"files": [
{
"id": "5",
"type": "file"
}
],
"path": "/var/mfile"
}
],
"name": "b"
}
]
I'm looking for output like this
a /tmp/file1 2
a /tmp/file2 3
b /tmp/tfile 4
b /var/mfile 5
What I have tried
cat myfile.json | jq '.[] | "\(.name), \(.devices[].path) \(.devices[].files[].id)"'
and result is
"a, /tmp/file1 2"
"a, /tmp/file2 2"
"a, /tmp/file1 3"
"a, /tmp/file2 3"
"b, /tmp/tfile 4"
"b, /var/mfile 4"
"b, /tmp/tfile 5"
"b, /var/mfile 5"
but I'm unable to select a key for the value is in parent map.
I want to for each of the elements matching name == a, for each of a[path], get a[path][id].
Using .devices[] twice will iterate over the same .devices twice, giving you the cartesian product. Extract from within one iteration.
You can nest string iterpolation; otherwise collect into an array (here you'd need , as separator) and use join(" ") to concatenate
Use the -r (or --raw-output) flag to output raw text
jq -r '.[] | "\(.name) \(.devices[] | "\(.path) \(.files[].id)")"'
a /tmp/file1 2
a /tmp/file2 3
b /tmp/tfile 4
b /var/mfile 5
Demo

JSONPath cannot query if condition is a child's property of array

I have a json as below. I want to select list reference of item which has method is "method A". But I cannot query with operator == (with Jayway JSONPath evaluation)
$.items[*].[?(#.methods[*].name == 'method A')].reference
But when I replace operator as contains, the jsonpath works.
$.items[*].[?(#.methods[*].name contains 'method A')].reference
I don't know why operator == does not work. Could anyone help me explain the reason?
JSON:
{
"name": "List codes",
"items": [
{
"name": "Cash",
"reference": "CA",
"methods": [
{
"name": "method A",
"id": "3543"
},
{
"name": "method B",
"id": "3544"
}
]
},
{
"name": "Debt",
"reference": "DE",
"methods": [
{
"name": "method C",
"id": "3545"
},
{
"name": "method B",
"id": "3544"
}
]
},
{
"name": "Property",
"reference": "PR",
"methods": [
{
"name": "method C",
"id": "3545"
},
{
"name": "method A",
"id": "3543"
}
]
}
]
}
#.methods[*].name will yield multiple results resulting into an array. You cannot use == to compare list of values with a string.
if you need to use == operator then you have to use the index along with ||
$.items[?(#.methods[0].name == 'method A' || #.methods[1].name == 'method A')].reference

Combining fields from multiple arrays to a single object

What I need is simply to extract id, sales from sorted_employees AND full name for that employee from employees.. like this:
{ "Full name": "John Doe", "ID": "employee1", "Sales": "26" }
{ "Full name": "Sam Jones", "ID": "employee2", "Sales": "119" }
What would be the easiest way to combine these 2 arrays ( employees & sorted_employees ) ? I have problems merging the result, I have tried to cast results into an array and I tried purely with jq filters.. but it doesn't want to give me what I need..
employees_id=$( echo "${json_data}" | jq -r '.employees[] | .id' )
sorted_employees_id=$( echo "${json_data}" | jq -r '.sorted_employees[] | .id' )
sorted_employees_sales=$( echo "${json_data}" | jq '.sorted_employees[] | .sales' )
{
"employees": [ {
"started_at": "2018-05-01 12.00",
"id": "employee1",
"facebook": "https://fb/john_doe",
"full name": "John Doe"
}, {
"started_at": "2017-05-01 12.00",
"id": "employee2",
"facebook": "https://fb/sam_jones",
"full name": "Sam Jones"
}, {
"started_at": "2016-05-01 12.00",
"id": "employee3",
"facebook": "https://fb/jane_roe",
"full name": "Jane Roe"
}],
"sorted_employees": [{
"id": "employee1",
"sales": 26
}, {
"id": "employee2",
"sales": 119
}, {
"id": "employee3",
"sales": 84
}]
}
You could combine the two arrays and just group by the common ID field and form the desired output object
jq '.employees + .sorted_employees | group_by(.id) |
map({"Full name": .[0]."full name", ID: .[0].id, "Sales": .[1].sales})'

JQ applying function to dict but print surrounding datas

I'd like
"1": {
"id": "1",
"type": "select",
"label": "country :",
"choices": {
"1": {
"label": "Canada CAN",
"value": "",
},
"2": {
"label": "United States USA",
"value": ""
}
}
}
to produce
"1": {
"id": "1",
"type": "select",
"label": "country :",
"choices": {
"1": {
"label": "Canada",
"value": "CAN",
},
"2": {
"label": "United States",
"value": "USA"
}
}
}
By now I have a two-step solution consisting of a sed-like function
def do_extract:
if .value | test("^$") then
(.value = (.label | capture(".* (?<code>...)")).code) | .label = (.label | capture("(?<name>.*) ...$").name)
else
.
end;
and a direct access to the sub-tree []."1"."choices"|keys_unsorted as $key|map_values(do_extract) but I have to manually copy-paste the output in place of the original "choices" dict.
Is there a way to do the function but still print the surrounding datas ?
Thanks
Well to get your desired result, you could do this:
.[].choices[] |= (.label | capture("^(?<label>.*?) (?<value>[^ ]+)$"))
To do that within your function, I'd change it to this:
def do_extract:
if .value == "" then
(.label | capture("^(?<label>.*?) (?<value>[^ ]+)$"))
else
.
end;
Then use it:
.[].choices[] |= do_extract

Resources