Nested Headings

Given a list of weighted headings, produce a nested ordered list in html.

“H1 All About Birds”,
“H2 Kinds of Birds”,
“H3 The Finch”,
“H3 The Swan”,
“H2 Habitats”,
“H3 Wetlands”

The above list should produce the following:

All About Birds
  1. Kinds of Birds
    1. The Finch
    2. The Swan
  2. Habitats
    1. Wetlands

You are given the following code to start with, and only the function toOutline(headings) should be implemented.

const headingData = [
  "H1 All About Birds",
  "H2 Kinds of Birds",
  "H3 The Finch",
  "H3 The Swan",
  "H2 Habitats",
  "H3 Wetlands",
  "H2 Kinds of Birds",
  "H3 The Finch",
  "H3 The Swan",
  "H2 Habitats",
  "H3 Wetlands",
  "H4 Zoo",
  "H4 Trees"
]

const Heading = function(weight, text) {
  this.weight = weight;
  this.text = text
};
const Node = function(heading, children) {
  this.heading = heading;
  this.children = children;
};

function toOutline(headings) {
    // Implement this function. Sample code below builds
    // an outline of only the first heading

    // let root = new Node(new Heading(0, ""), [new Node(headings[0], [])])
    // return root
}

// Parses a line of the input
// This implementation is correct for all predefined test cases
function parse(record) {
  const firstSpace = record.indexOf(" ");
  const weight = parseInt(record.substr(1, firstSpace));
  const text = record.substr(firstSpace + 1);
  return new Heading(weight, text);
}

// Converts a node to HTML
function toHtml(node) {
  let childHtml = "";
  if (node.children.length > 0) {
    childHtml = `<ol>${node.children.map(child => `<li>${toHtml(child)}</li>`).join("\n")}</ol>`;
  }
  const heading = node.heading.text.length === 0 ? "" : node.heading.text + "\n";
  return heading + childHtml;
}

const headings = headingData.map(r => parse(r));
const outline = toOutline(headings);
console.log(toHtml(outline[0]));

One solution is to recursively build Nodes, comparing the current heading’s weight to the previous heading’s weight.

function toOutline(headings) {
    let cur = headings[0]
    let nodes = buildNodes(cur, headings)
    return nodes
}

function buildNodes(prev, headings) {
  let nodes = []
  let cur = null

  while (headings.length > 0) {
    let cur = headings[0]

    if (cur.weight == prev.weight) {
      headings.shift()
      nodes.push(new Node(cur, []))
    }
    else if (cur.weight > prev.weight) {
      let childNodes = buildNodes(cur, headings)

      let n = nodes.pop() // remove previous and concat to it
      nodes.push(new Node(n.heading, childNodes))
    }
    else { // cur.weight < prev.weight
      return nodes
    }
  }

  return nodes
}

The solution produces the following results:

All About Birds
  1. Kinds of Birds
    1. The Finch
    2. The Swan
  2. Habitats
    1. Wetlands
  3. Kinds of Birds
    1. The Finch
    2. The Swan
  4. Habitats
    1. Wetlands
      1. Zoo
      2. Trees
Scroll to top