I’m looking to create a function for pagination that determines which page numbers to display (1 | 2 | 3 | 4 …).
I follow these specifications:
- The function, let’s call it
calculatePages, should return an array of page numbers.
- The inputs to the function are:
total: the total number of results.
perPage: the maximum number of items to display on each page.
page: the current page.
max: the maximum number of page numbers to display.
- Here are the rules the function should follow:
- Always display the maximum number of items unless the total number of pages (
totalPages) is less than max. In that case, display page numbers from page until totalPages.
- Start displaying from
page until max items (if available). For example, if page=5, max=10, and totalPages=8, display 5,6,7,8. If the number of displayed pages is less than max, display pages from 1 until the required number is reached.
- If
totalPages is more than max, start from page until the next max items, and if the number of displayed pages is less than max, go back to the beginning and add the difference to match max items.
Here’s what I did so far:
import { range } from 'lodash';
function calculatePages({ total, perPage, page, max }) {
const pages = Math.ceil(total / perPage);
const totalPages = [...Array(pages).keys()].map((i) => i + 1);
let start = page - 1;
const end = start + max > max ? start + (max - 1) : start + max;
if (end - start < max) {
start = start - (end - (end - start));
}
const results = totalPages.slice(start, end);
return results;
}
const tests = [
{ in: { total: 1, perPage: 4, page: 1, max: 10 }, out: [1] },
{ in: { total: 4, perPage: 4, page: 1, max: 10 }, out: [1] },
{ in: { total: 8, perPage: 4, page: 1, max: 10 }, out: [1, 2] },
{ in: { total: 8, perPage: 4, page: 2, max: 10 }, out: [1, 2] },
{
in: { total: 38, perPage: 4, page: 2, max: 10 },
out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},
{
in: { total: 40, perPage: 4, page: 2, max: 10 },
out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},
{
in: { total: 40, perPage: 4, page: 1, max: 10 },
out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},
{
in: { total: 40, perPage: 4, page: 9, max: 10 },
out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},
{
in: { total: 40, perPage: 4, page: 10, max: 10 },
out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},
{
in: { total: 90, perPage: 4, page: 1, max: 10 },
out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},
{ in: { total: 70, perPage: 4, page: 17, max: 10 }, out: range(9, 19) },
];
describe('calculatePages function', () => {
tests.forEach((t, i) => {
test(`#${i} ${JSON.stringify(t.in)}`, () => {
const a = calculatePages(t.in);
expect(t.out.length).toBeLessThanOrEqual(t.in.max);
expect(a.join(',')).toBe(t.out.join(','));
});
});
});
I’m encountering an issue with the test case where total=70, perPage=4, page=17, and max=10. It’s expected to return [9, 10, ..., 17, 18], but it’s not passing.