// With an iterative method controlled by "idx", we manipulate the timeline to identify "gaps" and insert "ads"
const splitTimeline = (arr, advArr, idx) => {
const res = [...arr]; // Create a shallow copy of the intermediate result array
const ad = advArr[idx]; // Direct access to the current iteration of "ad"
arr.forEach(
({ start, end, id }, rIdx) => { // De-structure and get the indexed element in the result array
if ( // Insert "ad" into existing "gap"
start < ad.start &&
end >= ad.end &&
id === 'gap'
) {
res.splice(rIdx, 1, { start, end: ad.start, id: 'gap'});
res.splice(rIdx + 1, 0, {
start: ad.start, end: Math.min(end, ad.end), id: ad.id
});
if (end > ad.end) res.splice(rIdx + 2, 0, {
start: ad.end, end, id: 'gap'
});
} else if ( // Handle edge-case where last "ad" exceeds timeline
idx === advArr.length - 1 && id === 'gap' &&
ad.start > start && ad.start < end
) {
res.splice(rIdx, 1, {
start: ad.start, end: end, id: ad.id
});
res.splice(rIdx, 0, {
start: start, end: ad.start, id: 'gap'
});
}
}
);
// Recurse if all "ads" have not been processed yet
if (idx < advArr.length - 1) return splitTimeline(res, advArr, idx + 1);
else return res;
};
// A method to fill "gaps" with "content" from the content-array ("carr")
const addContent = (tl, carr) => ( // "tl" represents the current timeline
tl.map(({ start, end, id }) => { // Iterate over the elements in the timeline
// If it's an "ad", simply return it as-is
if (id !== 'gap') return {start, end, id};
// Find a matching "content" id for the existing "gap"
return {start, end, id: carr.find(
ob => start >= ob.start && end <= ob.end
)?.id ?? "no content"}
})
);
// A simple method to construct, update a timeline, and place contents and ads within it
const placeAds = (cn, ad) => {
const cBgn = Math.min(...cn.map(({ start }) => start));
const cEnd = Math.max(...cn.map(({ end }) => end));
const initialTimeline = [{
start: cBgn, end: cEnd, id: 'gap'
}];
return (
addContent(
splitTimeline(
initialTimeline,
ad,
0
),
cn
)
);
};
const content = [
{start: 0, end: 50, id: 'C1'},
{start: 50, end: 100, id: 'C2'},
];
const ad = [
{start:10, end: 20, id: 'A1' },
{start:30, end: 60, id: 'A2' },
{start:80, end: 140, id: 'A3' },
];
console.log(placeAds(content, ad));
.as-console-wrapper { max-height: 100% !important; top: 0; }