PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Monday, August 1, 2022

[FIXED] How to move an item in a paginated array to the next page while respecting max page size limits?

 August 01, 2022     arrays, javascript, pagination     No comments   

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)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing