jq flatten list of objects into one object - jq

I want to go from
[
{"key_skjdghkbs": "deep house"},
{"key_kjsskjbgs": "deadmau5"},
{"key_jhw98w4hl": "progressive house"},
{"key_sjkh348vg": "swedish house mafia"},
{"key_js3485jwh": "dubstep"},
{"key_jsg587jhs": "escape"}
]
to
{
"key_skjdghkbs": "deep house",
"key_kjsskjbgs": "deadmau5"
"key_jhw98w4hl": "progressive house",
"key_sjkh348vg": "swedish house mafia",
"key_js3485jwh": "dubstep",
"key_jsg587jhs": "escape"
}
Each object in the original list has exactly one key but the keys are unique.
I could do something like jq .[] .genre if the keys were the same but they're not.

jq's add function does exactly this
jq 'add'

Try this (assuming your file is named so72297039.json):
jq '[.[] | to_entries] | flatten | from_entries' < so72297039.json
(Edit: OP edited question, so here's relevant answer)

Since duplicate keys are not possible (see other answer) you can use to format like this:
{
"artist": [
"deadmau5",
"swedish house mafia"
],
"genre": [
"deep house",
"progressive house",
"dubstep"
],
"song": [
"escape"
]
}
with a jq call like this:
jq '
map(to_entries)
| flatten
| group_by(.key)
| map({key: first.key, value: map(.value)})
| from_entries
' input.json
If the keys artist, genre, song are known in advance an easier to understand expression can be used.

Related

Print the key and a subset of fields if a field is not a specific value

I am new to jq and can't seem to quite get the syntax right for what I want to do. I am executing a command and piping its JSON output into jq. The structure looks like this:
{
"timestamp": 1658186185,
"nodes": {
"x3006c0s13b1n0": {
"Mom": "x3006c0s13b1n0.hsn.cm",
"Port": 15002,
"state": "free",
"pcpus": 64,
"resources_available": {
"arch": "linux",
"gputype": "A100",
"host": "x3006c0s13b1n0",
"mem": "527672488kb",
"ncpus": 64,
"ngpus": 4,
"system": "polaris",
"tier0": "x3006-g1",
"tier1": "g1",
"vnode": "x3006c0s13b1n0"
},
"resources_assigned": {},
"comment": "CHC- Offlined due to node health check failure",
"resv_enable": "True",
"sharing": "default_shared",
"license": "l",
"last_state_change_time": 1658175652,
"last_used_time": 1658175652
},
And so on with a record for each node. In psuedocode, what I want to do is this:
if state is not free then display nodename : {comment = "Why is the node down"}
The nodename is the key, but could be extracted from a field inside the record. However, for future reference, I would like to understand how to get the key. I figured out (I think) that you can't use == on strings, but instead have to use the regex functions.
This gives me the if state is not free part:
<stdin> | jq '.nodes[] | .state | test("free") | not'
This gives me an object with the Mom (which includes the key) and the comment:
jq '.nodes[] | {Mom: .Mom, comment: .comment}'
The question is how do I put all that together? And as for the keys, this gives me a list of the keys: jq '.nodes | keys' but that uses the non-array version of nodes.
One way without touching the keys would be to only select those array items that match the condition, and map the remaining items' value to the comment itself using map_values:
jq '.nodes | map_values(select(.state != "free").comment)'
{
"x3006c0s13b1n0": "CHC- Offlined due to node health check failure"
}
Keeping the whole comments object, which is closer to your desired output, would be similar:
jq '.nodes | map_values(select(.state != "free") | {comment})'
{
"x3006c0s13b1n0": {
"comment": "CHC- Offlined due to node health check failure"
}
}
Accessing the keys directly is still possible though. You may want to have a look at keys, keys_unsorted or to_entries.

flattening json to csv with jqplay

I'm trying to flatten a json file to .csv. I'd like to use jqplay for this in stead of programming it in python for example.
The example below is een array that als contains arrays.
My desired output is one line entry on the 2nd array:
so
OPEN, NR1, ....
CLOSED, NR2, ...
....
Can anyone help me with a good jq command for this?
[
{
"description": "Berendrechtsluis",
"lock_id": "BES",
"longitude_wgs84": 4.28561,
"latitude_wgs84": 51.34414,
"lock_doors": [
{
"state": "OPEN",
"lock_door_id": "NR1",
"operational_state": "NO_DATA",
"state_since_in_utc": "2021-12-29T16:32:23Z",
"longitude_wgs84": 4.28214,
"latitude_wgs84": 51.34426
},
{
"state": "CLOSED",
"lock_door_id": "NR2",
"operational_state": "WORKING",
"state_since_in_utc": "2022-01-12T12:32:52Z",
"operational_state_since_in_utc": "2021-12-22T13:13:57Z",
"longitude_wgs84": 4.28247,
"latitude_wgs84": 51.34424
},
....
Are you looking for something like this?
jq -r '.[].lock_doors[] | [.[]] | #csv'
"OPEN","NR1","NO_DATA","2021-12-29T16:32:23Z",4.28214,51.34426
"CLOSED","NR2","WORKING","2022-01-12T12:32:52Z","2021-12-22T13:13:57Z",4.28247,51.34424
Demo
To add column headers, simply prepend them in an array:
jq -r '["a","b","c"], .[].lock_doors[] | [.[]] | #csv'
"a","b","c"
"OPEN","NR1","NO_DATA","2021-12-29T16:32:23Z",4.28214,51.34426
"CLOSED","NR2","WORKING","2022-01-12T12:32:52Z","2021-12-22T13:13:57Z",4.28247,51.34424
Demo

Trying to get the correct output from JQ

I'm trying to get this output the device name "test"
My filter is .[] | [.deviceName] and it's returning error: (at :7): Cannot index array with string "deviceName"
{
"test": [
{
"deviceName": "test",
"monitoringServer": "server1"
}
]
}
Presumably you meant:
jq '.test[] | [.deviceName]'
or perhaps:
jq '.[][] | [.deviceName]'
but without knowing your requirements, it's hard to say. That's one of the reasons why the http://stackoverflow.com/help/mcve guidelines were formulated.

jq: find and replace within filter

I return a few json values from an API feed using this command:
curl -s -u user:pass -H 'my header' https://example.com/data.json | jq -cr '.[] | {id: .id, content: .content, assignee: .assignee.name}
I'm getting a CSV just as I need it, namely:
1235,"some text, sometimes with a comma, perhaps", "John Doe"
Everything is great, but command in the "content: .content" values are throwing off our processing of the data.
I'm trying to replace commas within the command, and not even sure what terminology to search for. Ideally I'd use something like this:
jq -cr '.[] | {id: .id, content: *** BEGIN DO SOMETHING .content END DO SOMETHING***, assignee: .assignee.name}
...I just don't know what that something is.
I'm guessing it's a gsub, but not sure how to isolate the syntax for .content.
Should waited the requisite 5 minutes.
Found my answer elsewhere:
jq -cr '.[] | {id: .id, content: .content | sub(","; ""), assignee: .assignee.name}
Yes you can use gsub/2.
Filter
. | gsub(",";"")
Input
1235,"some text, sometimes with a comma, perhaps", "John Doe"
Output
1235"some text sometimes with a comma perhaps" "John Doe"
Demo
https://jqplay.org/s/n5fFtBK7uj

How to do parse json array dynamically in shell script using jq too in shell script

Suppose I have the following json in a file json.txt
{
"first_name": "John",
"last_name": "Smith",
"things_carried": [
"apples",
"hat",
"harmonica"
],
"children": [
{
"first_name": "Bobby Sue",
"last_name": "Smith"
},
{
"first_name": "John Jr",
"last_name": "Smith"
}
]
}
In shell script I had written the logic to find the size of children array using jq tool .
size=cat json.txt | jq '.children | length'
i=0
while [ $i -le $size ]
do
array[$i]=$(cat json.txt | jq '.children[$i]')
i=`expr $i + 1`
done
On running this it gives the following error -
.children[$i] 1 compile error
It seems that it is not able to replace the variable i in the children[] array , as because if we give the expression -
array[$i]=$(cat json.txt | jq '.children[0]')
it runs well .
Can someone help me .
You're using single quotes around the jq program. Shells do not interpolate variables inside single quotes; this is intentional and the jq manual recommends using single quotes around programs for this reason.
An argument syntax is provided by jq for this purpose. This syntax allows you to set jq variables to the value of shell variables. You could replace your current jq invocation with this:
array[$i]=$(cat json.txt | jq --arg i $i '.children[$i | tonumber]')
It looks like you're just trying to set the children to a bash array variable.
You don't need to loop, just set the array directly.
$ IFS=$'\n'; array=($(jq -c '.children[]' json.txt))
You should use the following syntax:
array[$i]=$(cat json.txt | jq '.children['${i}']')

Resources