$ 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
jq
is 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