Created
March 2, 2021 01:40
-
-
Save meekg33k/023697d1f9d73597aedf6459d72c1657 to your computer and use it in GitHub Desktop.
Code snippet for Medium's algorithm problem on how to apply deltas to a post structure.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Ideally this will be declared in a constants module */ | |
const ADD_PARAGRAPH_DELTA = "addParagraph"; | |
const DELETE_PARAGRAPH_DELTA = "deleteParagraph"; | |
const UPDATE_PARAGRAPH_DELTA = "updateParagraph"; | |
let sectionParagraphIndices = []; | |
/** | |
* @param {string} delta | |
* @param {Array} paragraphs | |
* | |
* This method applies changes to a single paragraph | |
*/ | |
const applyDeltaToSingleParagraph = (delta, paragraphs) => { | |
const { type, paragraphIndex, paragraph } = delta; | |
let modifiedParagraphs = [...paragraphs]; | |
let text; | |
if (paragraph) { text = paragraph.text; } //DELETE has no text | |
switch (type) { | |
case ADD_PARAGRAPH_DELTA: | |
modifiedParagraphs.splice( | |
paragraphIndex, | |
0, | |
{ id: text, text } | |
); | |
updateSectionParagraphIndices(paragraphIndex, type); | |
break; | |
case DELETE_PARAGRAPH_DELTA: | |
modifiedParagraphs = [ | |
...modifiedParagraphs.slice(0, paragraphIndex), | |
...modifiedParagraphs.slice(paragraphIndex + 1) | |
]; | |
updateSectionParagraphIndices(paragraphIndex, type); | |
break; | |
case UPDATE_PARAGRAPH_DELTA: | |
modifiedParagraphs[paragraphIndex] = { id: text, text }; | |
break; | |
default: | |
break; | |
}; | |
return modifiedParagraphs; | |
}; | |
const applyDeltaToParagraphs = (paragraphs, deltas) => { | |
if (deltas && deltas.length > 0 && paragraphs) { | |
deltas.forEach((delta) => { | |
const { paragraphIndex } = delta; | |
if (paragraphIndex >= 0) { | |
paragraphs = applyDeltaToSingleParagraph(delta, paragraphs); | |
} | |
}); | |
}; | |
return paragraphs; | |
} | |
/** | |
* @param {Number} paragraphIndex | |
* @param {String} type | |
* | |
* This function updates a paragraph with the section indices | |
*/ | |
const updateSectionParagraphIndices = (paragraphIndex, type) => { | |
sectionParagraphIndices.forEach((sectionParagraphIndex) => { | |
const [start, end] = sectionParagraphIndex; | |
if (paragraphIndex <= end) { | |
if (paragraphIndex >= start) { | |
//This is the changed section | |
if (type === ADD_PARAGRAPH_DELTA) { sectionParagraphIndex[1] = end + 1; } | |
if (type === DELETE_PARAGRAPH_DELTA) { sectionParagraphIndex[1] = end - 1; } | |
} | |
else { | |
//Update affected sections because of preceding change | |
if (type === ADD_PARAGRAPH_DELTA) { | |
sectionParagraphIndex[0] = start + 1; | |
sectionParagraphIndex[1] = end + 1; | |
} | |
if (type === DELETE_PARAGRAPH_DELTA) { | |
sectionParagraphIndex[0] = start - 1; | |
sectionParagraphIndex[1] = end - 1; | |
} | |
} | |
} | |
}); | |
} | |
/** | |
* | |
* @param {Array} sections | |
* @param {String} paragraphLength | |
* | |
* This function returns an array of start and end | |
* positions for each section in the post content.e.g [[0, 2], [4,5]] | |
*/ | |
const constructSectionParagraphIndices = (sections, paragraphLength) => { | |
let sectionStartEndPositions = []; | |
if (sections && sections.length > 0) { | |
sections.forEach((section, index) => { | |
const currSectionStartIndex = section.startIndex; | |
if (index + 1 < sections.length) { | |
const currSectionEndIndex = sections[index + 1].startIndex - 1; | |
sectionStartEndPositions.push([currSectionStartIndex, currSectionEndIndex]) | |
} | |
else { | |
sectionStartEndPositions.push([currSectionStartIndex, paragraphLength - 1]); | |
} | |
}); | |
}; | |
return sectionStartEndPositions; | |
}; | |
const constructParagraphBySectionData = (paragraphs) => { | |
const SECTION_DELIMITER = '-'; | |
const sectionParagraphLength = sectionParagraphIndices.length; | |
let paragraphsBySection = []; | |
if (sectionParagraphIndices && sectionParagraphLength > 0) { | |
sectionParagraphIndices.forEach((sectionParagraphIndex, index) => { | |
const [start, end] = sectionParagraphIndex; | |
paragraphsBySection.push(...paragraphs.slice(start, end + 1)); | |
if (index < sectionParagraphLength - 1 && start <= end) { | |
paragraphsBySection.push({ text: SECTION_DELIMITER }); | |
} | |
}); | |
}; | |
//Edge case: Check if last character is section delimiter | |
if (paragraphsBySection[paragraphsBySection.length - 1].text === SECTION_DELIMITER) { | |
paragraphsBySection = paragraphsBySection.slice(0, paragraphsBySection.length - 1); | |
} | |
return paragraphsBySection; | |
}; | |
/** | |
* @param {Array} paragraphs | |
* | |
* This function constructs the final text to | |
* be rendered on the page | |
*/ | |
const constructFinalPageMarkup = (paragraphs) => { | |
const PARAGRAPH_DELIMITER = '\n'; | |
if (paragraphs && paragraphs.length > 0) { | |
return paragraphs.reduce((acc, paragraph) => { | |
if (paragraph.text) { | |
const markup = acc ? `${acc}${PARAGRAPH_DELIMITER}${paragraph.text}` : `${paragraph.text}`; | |
return markup; | |
} | |
}, ''); | |
} | |
else { return '' }; | |
}; | |
/** | |
* @param {Object} postContent | |
* | |
* The main function the parses the post content structure | |
*/ | |
const parsePostContent = (postContent) => { | |
const clonedPostContent = { ...postContent }; | |
const { paragraphs, sections } = clonedPostContent; | |
let paragraphsBySectionData = paragraphs; | |
let pageMarkup = ''; | |
if (paragraphs && paragraphs.length > 0) { | |
sectionParagraphIndices = constructSectionParagraphIndices(sections, paragraphs.length); | |
} | |
if (deltas && deltas.length > 0) { | |
paragraphsBySectionData = applyDeltaToParagraphs( | |
paragraphs, | |
deltas, | |
sectionParagraphIndices | |
); | |
} | |
paragraphsBySectionData = constructParagraphBySectionData(paragraphsBySectionData, sectionParagraphIndices); | |
pageMarkup = constructFinalPageMarkup(paragraphsBySectionData); | |
return pageMarkup; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment