Issue
I have generated a paginated array (2 dimensional) with a maximum item limit in each page. I would like to alter the array to move an item from one page to the next. When that happens it should push any overflow items to the next page and cascade that effect until the last page has enough room. Does anyone have an idea how I can achieve this?
Simplified Example
const maxAmount = 6;
const pages = [
[1,2,3,4,5,6], //page 1
[7,8,9,10,11,12], //page 2
[13,14,15,16,17,18], //page 3
[19,20] // page 4
];
I would like to move the last item from page 1 to the front of the next page 2. That should cascade through the pages so now the last item of page 2 should appear at the front of page 3 and so on.
The result would look like this:
[
[1,2,3,4,5], //page 1
[6,7,8,9,10,11], //page 2
[12,13,14,15,16,17], //page 3
[18,19,20] // page 4
];
Use Case
I am generating elements on each page that have variable height but need to appear on a single fixed height page without cutting anything off (pdf export). So I test the height of the elements and if there is overflow the last element should appear on the next page, shifting everything that follows to new pages if they also have overflow. I have a recursive function set up to continue testing heights until there is no overflow, but it needs this array transform described above to work.
What I have tried
This is an iterative approach using array reduce to check if a page has overflow and then it removes that item and prepends it to the next page in the array. My mind breaks down when it comes to cascading that effect through the rest of the pages.
For additional complexity each item might be a parent or "Theme" item and child items always need a theme item preceding it. So, we could potentially be moving a child and its parent to the next page. This effect also needs to cascade down.
But please don't focus on the extra complexity when answering. Use the simplified example and I can figure out the rest.
updatePdfPages(overflowPages: number[]): ItemBreakout[][] {
let addItemsForNextPage: ItemBreakout[] = [];
this.pdfPages.reduce((resultArray: ItemBreakout[][], page: ItemBreakout[], i) => {
if (addItemsForNextPage.length > 0) {
page.unshift(...addItemsForNextPage);
addItemsForNextPage = [];
}
if (overflowPages.includes(i)) {
const removedItem = page.pop();
const themeId = removedItem.themeId;
let parentTheme: ItemBreakout;
if (page[page.length - 1].id === themeId) {
parentTheme = page.pop();
} else {
parentTheme = page.find(item => item.id === themeId);
}
addItemsForNextPage.push(parentTheme, removedItem);
}
resultArray.push(page);
if (!this.pdfPages[i + 1]) {
resultArray.push(addItemsForNextPage);
}
return resultArray;
}, []);
}
So, yeah... has anyone done something like this before?
Solution
Here's a simplified version of the answer I came up with:
const movePageItems = (pageIndex, numItems, pages, maxItemsPerPage) => {
let addItemsForNextPage = [];
return pages.reduce((resultArray, page, i) => {
if(addItemsForNextPage.length > 0){
//if there are items to add from the previous page, push them to the front of this page
page.unshift(...addItemsForNextPage);
addItemsForNextPage = [];
}
if(pageIndex === i || page.length >= maxItemsPerPage){
//transfer x number of items from the target page
//or transfer x number of excess items beyond the max item limit from a non target page
addItemsForNextPage = pageIndex === i ?
page.splice((page.length - 1) - numItems, numItems) :
page.splice(maxItemsPerPage - 1, page.length);
}
resultArray.push(page);
return resultArray;
}, []);
}
So using the example from the question above:
const maxAmount = 6;
const pages = [
[1,2,3,4,5,6], //page 1
[7,8,9,10,11,12], //page 2
[13,14,15,16,17,18], //page 3
[19,20] // page 4
];
const updatedPages = movePageItems(0, 1, pages, maxAmount);
/*
updatedPages would equal the following
[
[1,2,3,4,5], //page 1
[6,7,8,9,10,11], //page 2
[12,13,14,15,16,17], //page 3
[18,19,20] // page 4
];
*/
And here is the full code I wound up using to accommodate the added complexity of parent items if anyone is interested:
updatePdfPages(overflowPage: number): ItemBreakout[][] {
let addItemsForNextPage: ItemBreakout[] = [];
return this.pdfPages.slice().reduce((resultArray: ItemBreakout[][], page: ItemBreakout[], i) => {
if (addItemsForNextPage.length > 0) {
const filteredOutDuplicates = addItemsForNextPage.filter(newItem => !page.includes(newItem));
if (filteredOutDuplicates.length !== addItemsForNextPage.length) {
addItemsForNextPage = filteredOutDuplicates.filter(newItem => {
const themeIndex = page.findIndex(item => item.id === newItem.themeId);
if (themeIndex > -1) {
page.splice(themeIndex + 1, 0, newItem);
return false;
} else {
return true;
}
});
}
page.unshift(...addItemsForNextPage);
addItemsForNextPage = [];
}
if (overflowPage === i || page.length >= this.maxPageCount) {
const removedItems = overflowPage === i ? [page.pop()] : page.splice(this.maxPageCount - 1, page.length);
const themeId = removedItems[0].themeId;
const parentTheme = page.find(item => item.id === themeId);
if (parentTheme) {
if (page[page.length - 1].id === themeId) {
removedItems.unshift(page.pop());
} else {
removedItems.unshift(parentTheme);
}
}
addItemsForNextPage = removedItems;
}
resultArray.push(page);
if (!this.pdfPages[i + 1] && addItemsForNextPage.length > 0) {
resultArray.push(addItemsForNextPage);
}
return resultArray;
}, []);
}
Answered By - Jake Zeitz Answer Checked By - Timothy Miller (PHPFixing Admin)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.