Kevin
Kevin

Reputation: 1009

Create a tree using numbers of h1...h6 tags in a DIV

I've been struggling to find a solution for this for a few hours. I've made some attempts and scratched them, my closest attempt is at the end of this question..

Assume I wish to build a table of contents using h1...h6 tags, I don't have any nesting using divs, etc. The tag is what informs the table of contents if it is a parent or child.

For example, a h1 then a h2 means the h2 is a child of the h1.

Lets assume we have an array of elements:

// omitting other properties like 'text', etc for brevity.
[{ tag: 2 }, { tag: 2 }, { tag: 3 }, { tag: 3 }, { tag: 4 }, { tag: 2 }]

This would represent the following HTML:

<div>
    <h2>
    <h2>
    <h3>
    <h3>
    <h4>
    <h2>
</div>

And we are after a tree that looks like this:

[{
    tag: 2
}, {
    tag: 2
    children: [{
        tag: 3
    }, {
        tag: 3,
        children: [{
            tag: 4
        }]
    }]
}, {
    tag: 2
}]

It shouldn't be too difficult, but I just can't wrap my head around it. Instead of a parent reference (as most other questions have), I am strictly using the number value to signify if its a parent.

Here's the code I've come up with that works but only in very limited circumstances. For example, if there is a sibling after the last element, it doesn't work.

Thank you!

//Loop through the array backwards
let newArr = [];
let tempArr = [];
while (arr.length) {    
    if (arr.length === 1) {
        newArr.unshift(arr.pop())
    } else {
        let item = arr.pop();

        if (tempArr.length == 0) {
            tempArr.push(item);
            item = arr.pop();
        }

        //Check if the items tag is equal to the tempArrs last item tag
        if (item.tag == tempArr[tempArr.length - 1].tag) {
            tempArr.push(item)
        } else if (item.tag < tempArr[tempArr.length - 1].tag) {



            item.children = tempArr;

            //there might be something in newArr...
            if (newArr.length && newArr[0].tag > item.tag) {
                item.children.push(newArr.pop());
            }

            newArr.unshift(item)
            tempArr = [];
        }
    }
}

Upvotes: 1

Views: 334

Answers (1)

Nina Scholz
Nina Scholz

Reputation: 386522

You could use a helper array for the levels and take an offset for starting tag (assuming no smaller/parent tags are available).

var array = [{ tag: 2 }, { tag: 2 }, { tag: 3 }, { tag: 3 }, { tag: 4 }, { tag: 2 }],
    offset = -array[0].tag;
    result = [],
    levels = [{ children: result }];

array.forEach(function (o) {
    levels[o.tag + offset].children = levels[o.tag + offset].children || [];
    levels[o.tag + offset].children.push(levels[o.tag + offset + 1] = o);
});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions