'Transform a one dimensional list to a multidimentional (parent/child) list based on element value

I'm working with a piece of data and need to structure it into a parent/child kinda thing.

Here is the data I have. The title and content have been modified for the example. The level key is the main thing here as far as the data is concerned.

[
    {
        "content": null,
        "level": 1,
        "number": null,
        "title": "Level 1 title"
    },
    {
        "content": null,
        "level": 2,
        "number": "1",
        "title": "Level 2 title (1)"
    },
    {
        "content": "Level 3 content (1.1)",
        "level": 3,
        "number": "(ހ)",
        "title": null
    },
    {
        "content": "Level 3 content (1.2)",
        "level": 3,
        "number": "(ހ)",
        "title": null
    },
    {
        "content": "Level 3 content (1.3)",
        "level": 3,
        "number": "(ނ)",
        "title": null
    },
    {
        "content": null,
        "level": 2,
        "number": "2",
        "title": "Level 2 title (2)"
    },
    {
        "content": "Level 3 content (2.1)",
        "level": 3,
        "number": "(ހ)",
        "title": null
    },
    {
        "content": "Level 3 content (2.2)",
        "level": 3,
        "number": "(ހ)",
        "title": null
    },
]

What I want to do is read through this list and end up with the following

[
    {
        "content": null,
        "level": 1,
        "number": null,
        "title": "Level 1 title",
        "children": [
            {
                "content": null,
                "level": 2,
                "number": "1",
                "title": "Level 2 title (1)",
                "children": [
                    {
                        "content": "Level 3 content (1.1)",
                        "level": 3,
                        "number": "(ހ)",
                        "title": null
                    },
                    {
                        "content": "Level 3 content (1.2)",
                        "level": 3,
                        "number": "(ހ)",
                        "title": null
                    },
                    {
                        "content": "Level 3 content (1.3)",
                        "level": 3,
                        "number": "(ނ)",
                        "title": null
                    }
                ]
            },

            {
                "content": null,
                "level": 2,
                "number": "2",
                "title": "Level 2 title (2)",
                "children": [
                    {
                        "content": "Level 3 content (2.1)",
                        "level": 3,
                        "number": "(ހ)",
                        "title": null
                    },
                    {
                        "content": "Level 3 content (2.2)",
                        "level": 3,
                        "number": "(ހ)",
                        "title": null
                    }
                ]
            }
        ]
    },
]

What I've tried

I have tried to group all items where the level is the same and append it to the parent. But then I immediately get confused and come to a full stop.

Below is my attempt at it, while trying to keep track of the current and parent section levels and indexes.

parent_section = None
parent_section_idx = 0
current_level = 0
next_level = 0

for i, section in enumerate(chunk):
    current_level = section.level

    if section.level == current_level:
        parent_section = section
        parent_section_idx = i

    if section.level == next_level:
        chunk[parent_section_idx].children.append(section)
        # parent_section.children.append(section)

    if section.level != current_level:
        next_level += 1
        parent_section = section

I couldn't find questions about this particular scenario. Most of the similar questions are going from multidimensional to one dimensional. Excuse me for my lack of research.



Solution 1:[1]

So I don't know exactly if that can help you, but here is a (ugly) example of what could kinda work for what you would wish to accomplish :

def imbricate(sections_list):
    index_at = 0

    def sub_imbricate(child_list, at_level):
        nonlocal index_at
        nonlocal sections_list

        while index_at < len(sections_list):
            section = sections_list[index_at]
            if section['level'] > at_level:
                if not 'children' in child_list[-1].keys():
                    child_list[-1]['children'] = []
                sub_imbricate(child_list[-1]['children'], section['level'])
            elif section['level'] == at_level:
                child_list.append(section.copy())
                index_at += 1
            elif section['level'] < at_level:
                return child_list

        return child_list

    return sub_imbricate([], 1)

It can be done better, and doesn't handle exceptions, but maybe can you get a better understanding of how to reach your objective from that snippet.

I have a bit of confusion as to whether or not there should be cases where two successive entry can have their "level" attribute differ by more than one.

Regardless you just need to recursively pass through the whole list by increasing or decreasing your "level" of imbrication, from what I can understand.

Note : you probably already know but to test that JSON in python without unpacking it, you can just replace the null values with "None" and voilĂ  theWholeThing = <copyPaste>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 nextoptionnone