$ jq -r '.userinfo | keys[] as $k | [ $k, .[$k][] ] | @tsv' file
alice 1 2
bob 11 22
This assumes that the input in file is a valid JSON document, such as
{
"userinfo": {
"alice": {
"key1": 1,
"key2": 2
},
"bob": {
"key1": 11,
"key2": 22
}
}
}
The jq expression picks out the userinfo top-level object and then iterates over the keys of this object, with each key being assigned to the internal variable $k. It is specifically the [] of keys[] that causes the looping over all the keys (usernames).
For each iteration, the array [ $k, .[$k][] ] is constructed, i.e. an array consisting of the key itself followed by the values in the userinfo object associated with that particular key (regardless of what the keys under .[$k] might be or how many there may be, i.e. we ignore the keys key1 and key2 themselves).
The constructed array is then passed through the @tsv operator which outputs it as a tab-delimited record.
For this to make any sense, we assume that the keys are ordered the same way for each object under userinfo. If that's not the case, you may want to sort the keys by passing the data through jq -S . first. If sub-objects have a varying set of keys (some may have a key3 and others may lack key1), then the output is still unlikely to make sense as you have no header on the fields in the output and no indication that a key is missing in an object.
We can take care of generating headers for each field based on the key names in the sub-objects (and using name as the header for the username field) with a somewhat more complicated jq expression:
(
[.userinfo[] | keys[]] | unique
) as $h |
This creates the header using "name" and the unique keys
from each sub-object:
[ "name", $h[] ],
Now we need a "double loop" over each user object and
the unique keys (in $h) that we extracted previously,
basically: for each user, create an array consisting of
the username and the value for each of the keys
(the inner loop):
[
.userinfo as $u |
$u |
# Outer loop:
keys[] as $k |
[
# The username:
$k,
# Inner loop over the $h array, extracting
# either useful data or nothing depending on
# whether the key exists in this object.
($h[] | $u[$k][.])
]
]
| @tsv
Example input with unsorted and missing/extra keys:
{
"userinfo": {
"alice": {
"key2": 2,
"key1": 1
},
"bob": {
"key1": 11,
"key2": 22
},
"mallory": {
"key1": 111,
"key3": 333
}
}
}
Output:
name key1 key2 key3
alice 1 2
bob 11 22
mallory 111 333
jqis a really really powerful tool. It's like learning a whole new programming language. Whenever I think I learned everything about it there are still some new things :) – Bog Mar 19 '24 at 15:09