Waseem Hassan
Waseem Hassan

Reputation: 231

Parse Markdown headings to generate a nested list in JavaScript

I've been trying to nest headings based on the number of "#". I parsed markdown and extracted all the headings. They're in following format

# Getting Started
# Heading 1
## SubHeading 1
## SubHeading 2
### SubSubHeading 1
### SubSubHeading 2
#### SubSubSubHeading 1
## SubHeading 3

I tried to recursively convert it to a nested ul list but it doesn't seem to work.. This is what i did so far

function createRepresentationFromHeadings(headings, index, level) {
    var data = "<ul>";
    for (var i = index; i < headings.length; i++) {
        var heading = headings[i];
        var parts = heading.split("# ");
        parts[0] = parts[0] + "#";
        data += "<li>" + parts[1];
        if (i + 1 < headings.length) {
            var nextParts = headings[i + 1].split("# ");
            nextParts[0] = nextParts[0] + "#";
            if (determineHeadingLevel(nextParts[0]) > level)
                createRepresentationFromHeadings(headings, i + 1, determineHeadingLevel(nextParts[0]));
        }
        data += "</li>";
    }
    data += "</ul>";
    return data;
}

function determineHeadingLevel(part) {
    return part.length;
}

It just creates a flattened ul. Here's a fiddle: https://jsfiddle.net/vrt2889q/

Upvotes: 3

Views: 1755

Answers (1)

trincot
trincot

Reputation: 350961

You could use this recursive (sub) function:

function createRepresentationFromHeadings(headings) {
    let i = 0;
    const tags = [];
    
    (function recurse(depth) {
        let unclosedLi = false;
        while (i < headings.length) {
            const [hashes, data] = headings[i].split("# ");
            if (hashes.length < depth) {
                break;
            } else if (hashes.length === depth) {
                if (unclosedLi) tags.push('</li>');
                unclosedLi = true;
                tags.push('<li>', data);
                i++;
            } else {
                tags.push('<ul>');
                recurse(depth+1);
                tags.push('</ul>');
            }
        }
        if (unclosedLi) tags.push('</li>');
    })(-1);
    return tags.join('\n');
}

var headings = [
    "# Getting Started",
    "# Heading 1",
    "## SubHeading 1",
    "## SubHeading 2",
    "### SubSubHeading 1",
    "### SubSubHeading 2",
    "#### SubSubSubHeading 1",
    "## SubHeading 3",
];

document.getElementById("test").innerHTML = 
    createRepresentationFromHeadings(headings);
<div id="test"></div>

Upvotes: 1

Related Questions