如何在遵守最大页面大小限制的同时将分页数组中的项目移动到下一页?

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

我生成了一个分页数组(二维),每页都有最大项目限制。我想更改数组以将项目从一页移动到下一页。当发生这种情况时,它应该将任何溢出项目推送到下一页并级联该效果,直到最后一页有足够的空间。有谁知道我如何才能做到这一点?

简化示例

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
];

我想将第 1 页的最后一个项目移到下一个第 2 页的前面。这应该在页面之间层叠,所以现在第 2 页的最后一个项目应该出现在第 3 页的前面,依此类推.

结果如下所示:

[
  [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
];

用例

我在每个页面上生成具有可变高度但需要出现在单个固定高度页面上而不切断任何内容的元素(pdf 导出)。所以我测试了元素的高度,如果有溢出,最后一个元素应该出现在下一页,如果它们也有溢出,则将后面的所有内容转移到新页面。我设置了一个递归函数来继续测试高度,直到没有溢出,但它需要上述数组转换才能工作。

我试过的

这是一种迭代方法,使用数组 reduce 检查页面是否溢出,然后删除该项目并将其添加到数组中的下一页。当涉及到将这种效果级联到其余页面时,我的思绪崩溃了。

对于额外的复杂性,每个项目可能是父项目或“主题”项目,而子项目始终需要在它之前有一个主题项目。因此,我们可能会将子项及其父项移动到下一页。这个效果也需要层叠下来。

但请不要在回答时关注额外的复杂性。使用简化的示例,我可以找出其余部分。

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;
  }, []);
}

所以,是的...以前有人做过这样的事情吗?

这是我想出的答案的简化版本:

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;
  }, []);
}

所以使用上面问题的例子:

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
];
*/

如果有人感兴趣,这里是我最后用来适应父项的复杂性的完整代码:

    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;
        }, []);
    }