I have a large text from which heading strings have been extracted using regex. The headings start with from 1-6 hashtags (#). Here is an example of the input array:
const content = [
"#1",
"##1a",
"###1a1",
"###1a2",
"##1b",
"#2",
"#3",
"##3a",
"##3b",
"#4",
];
The heading level (number of hashtags at the beginning of the string) describes where in the hierarchy of the chapters a certain heading is. I would like to parse my input into an array of heading objects, which contain the heading text without the hashtag and the heading nested chapters. The desired output for the array above is:
export interface Heading {
chapters: Heading[];
text: string;
}
const headings: Heading[] = [
{
text: "1",
chapters: [
{
text: "1a",
chapters: [
{ text: "1a1", chapters: [] },
{ text: "1a2", chapters: [] },
],
},
{ text: "1b", chapters: [] },
],
},
{ text: "2", chapters: [] },
{
text: "3",
chapters: [
{ text: "3a", chapters: [] },
{ text: "3b", chapters: [] },
],
},
{ text: "4", chapters: [] },
];
I tried writing a function that parses the string but got stuck on how to know which heading output the current string belongs to:
export const getHeadings = (content: string[]): Heading[] => {
let headingLevel = 2;
let headingIndex = 0;
const allHeadings = content.reduce((acc, currentHeading) => {
const hashTagsCount = countHastags(currentHeading);
const sanitizedHeading = currentHeading.replace(/#/g, "").trim();
const heading = {
chapters: [],
text: sanitizedHeading,
};
if (hashTagsCount === headingLevel) {
headingIndex = headingIndex + 1;
} else {
headingIndex = 0;
}
headingLevel = hashTagsCount;
if (hashTagsCount === 2) {
acc.push(heading);
} else if (hashTagsCount === 3) {
if (acc.length === 0) {
return acc;
}
if (acc.length === 1) {
acc[acc.length - 1]["chapters"].push(heading);
}
} else if (acc.length === 2) {
acc[acc.length - 1]["chapters"][headingIndex]["chapters"].push(heading);
} else if (acc.length === 3) {
acc[acc.length - 1]["chapters"][headingIndex]["chapters"][headingIndex][
"chapters"
].push(heading);
}
return acc;
}, []);
return allHeadings;
};
While this works for a very simple case, it is not scalable and has a predefined level of headings (with the if statements). How can I rewrite this in a way that the number of levels (hashtags) does not matter?