Skip to content

Instantly share code, notes, and snippets.

@james4388
Last active January 8, 2020 00:06
Show Gist options
  • Save james4388/aaeba4032f13555aa5766284648d955a to your computer and use it in GitHub Desktop.
Save james4388/aaeba4032f13555aa5766284648d955a to your computer and use it in GitHub Desktop.
Wrap HTML Tag to a string for given ranges
/*
apply all tag from tags to the string s
const tags = [
[0, 4, 'i'], // start at index, end at index, style
[6, 9, 'b'],
[7, 10, 'u']
]
const s = 'Hello World!'
return html string: <i>Hell</i>o <b>W<u>orl</u></b><u>d</u>!
At one index there will be only one open or close;
Tag does not need to be valid
*/
function wrapTags(tags, s) {
const openAt = new Map();
const closeAt = new Map();
for (const [open, close, tag] of tags) {
openAt.set(open, tag);
closeAt.set(close, tag);
}
const html = [];
const openStack = [];
for (let i = 0; i < s.length; i += 1) {
if (openAt.has(i)) {
const openTag = openAt.get(i);
openStack.push(openTag);
html.push(`<${openTag}>`);
}
html.push(s[i]);
if (closeAt.has(i)) {
const closeTag = closeAt.get(i);
const tmpStack = [];
while (openStack.length > 0 && openStack[openStack.length - 1] !== closeTag) {
const tmpStyle = openStack.pop();
html.push(`</${tmpStyle}>`);
tmpStack.push(tmpStyle);
}
if (openStack.length > 0 && openStack[openStack.length - 1] === closeTag) {
html.push(`</${closeTag}>`);
openStack.pop();
}
while (tmpStack.length > 0) {
const tmpStyle = tmpStack.pop();
html.push(`<${tmpStyle}>`);
openStack.push(tmpStyle);
}
}
}
while (openStack.length > 0) {
const closeTag = openStack.pop();
html.push(`</${closeTag}>`);
}
return html.join('');
}
/*
Time complexity:
n = s.length
m = tags.length
h = total nested tag which maximum is n / 2
O(n*h) = O(n*n/2) ~ O(n ^ 2) . :(
Space complexity: O(m + n)
O(m) to store ranges
O(html.length) ~= O(n + m) to store result (which should not count...)
O(n/2) to store openStack (most nested tag can be open at the same time):
*/
/* Test cases */
const s = 'Hello World!';
console.assert(wrapTags([], s) === s, "Empty styles not working");
console.assert(wrapTags([
[0, 0, 'i'],
[1, 1, 'b'],
[2, 2, 'u']
], s) === '<i>H</i><b>e</b><u>l</u>lo World!', "Non overlap");
console.assert(wrapTags([
[0, 11, 'i'],
[1, 10, 'b'],
[2, 9, 'u']
], s) === '<i>H<b>e<u>llo Worl</u>d</b>!</i>', "Contained");
console.assert(wrapTags([
[0, 11, 'i'],
[1, 10, 'b'],
[2, 9, 'u'],
[3, 8, 'i'],
[4, 7, 'b'],
[5, 6, 'c'],
], s) === '<i>H<b>e<u>l<i>l<b>o<c> W</c>o</b>r</i>l</u>d</b>!</i>', "Contained 2");
console.assert(wrapTags([
[0, 5, 'i'],
[1, 7, 'i']
], s) === '<i>H<i>ello </i>Wo</i>rld!', "Overlap same");
console.assert(wrapTags([
[0, 5, 'i'],
[1, 6, 'u'],
[2, 7, 'b'],
[3, 8, 'a']
], s) === '<i>H<u>e<b>l<a>lo </a></b></u></i><u><b><a>W</a></b></u><b><a>o</a></b><a>r</a>ld!', "Complex 1");
console.assert(wrapTags([
[0, 5, 'a'],
[1, 6, 'b'],
[2, 7, 'c'],
[3, 8, 'd'],
[4, 9, 'e'],
[5, 10, 'f'],
[6, 11, 'g']
], s) === '<a>H<b>e<c>l<d>l<e>o<f> </f></e></d></c></b></a><b><c><d><e><f><g>W</g></f></e></d></c></b><c><d><e><f><g>o</g></f></e></d></c><d><e><f><g>r</g></f></e></d><e><f><g>l</g></f></e><f><g>d</g></f><g>!</g>', "Complex 2");
@james4388
Copy link
Author

Not sure the produced html is optimal or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment