Markdown has become quite the standard for static contents like blog posts, notes, readmes, etc as popularized by static site generators (SSGs), with a plethora of packages that can parse and transform markdown content into HTML for rendering. Most content written in markdown have multiple layers of headings, hence it is often required that they be rendered alongside a table of contents (hierarchy of headings) to ease navigation through the content.
Consider a blog being developed using one of the popular SSGs for which all blog posts are written in markdown and a parser is used to transform all markdown content to flat (no nested elements) HTML markup for rendering. Given a markdown that has been transformed to HTML, you are required to build a hierarchy of headings that can be used to render a table of contents alongside the content of the markdown.
Since the HTML markup has a flat structure, it is easy to get all the heading elements (h1, h2, h3, h4, h5, h6
) and store them in an array as a flat list. To keep things simple, each heading in the list will be stored as an array of two elements — the first being an integer in the range 1 - 6 (both inclusive) that represents the heading level (1 for h1
, 2 for h2
, etc), while the second is an object containing metadata about the heading like its title, id (for anchored navigation), etc.
Here is some sample code to build up the flat list of headings as described:
const listOfHeadings = Array.from(
document.querySelectorAll("h1, h2, h3, h4, h5, h6"),
elem => [
parseInt(elem.tagName.slice(-1)), // (int) heading level
{ id: elem.id, title: elem.textContent } // (record) heading metadata
]
);
Given a flat list of headings, a function can be written that rebuilds the flat list into a nested list based on heading hierarchy. Rebuilding the flat list into the nested list can be done in-place (to reduce auxiliary space requirements).
function hierarchyOfHeadings(listOfHeadings) {
// your code here
}