jq

How do I turn tagged recursive object into pairs of tag and absolute path?


How do I turn a recursive object { id, name: { data }, children: self[] } into array of something like { id: id, name: name.data | join('/') } ?

For example, I'd like to convert given following data

[
    {"id": "1", "name": { "data": "foo" }, "children": [] },
    {"id": "2", "name": { "data": "bar" }, "children": [
        {"id": "4", "name": { "data": "melon" }, "children": [
            {"id": "5", "name": { "data": "soda" }, "children": [] }
        ] }
    ] },
    {"id": "3", "name": { "data": "baz" }, "children": [
        {"id": "6", "name": { "data": "my data" }, "children": [] }
    ] }
]

into:

{"id": "1", "name": "foo"},
{"id": "2", "name": "bar"},
{"id": "4", "name": "bar/melon"},
{"id": "5", "name": "bar/melon/soda"},
{"id": "3", "name": "baz"},
{"id": "6", "name": "baz/my data"},

I don't mind orders of element, because each id of given object have unique value in each input.

My attempt is

.. | (.children? // empty) | .[] | { id: .id, name: .name.data }

but this yields name of the single layer (i.e. foo, bar, melon, ...).


Solution

  • Your input is not valid due to a few extra commas. Otherwise, using a recursive function will get expected result:

    jq -c 'def down($parents):
               ($parents + .name.data) as $pass |
               { id: .id, name: $pass }, (.children[]|down($pass + "/"));
           .[] | down("")' input.json