Calculating pagination bounds for display
Status: Done
Confidence: Certain
Math in this page not rendering? See the fix
tl;dr: How do you calculate the limits for a
pagination widget like “« 4 5 6 7 »”? Number the pages starting from 0.
Then the first page to display
and one page after the last page to display
(4 and 8 in the example above) are given by
where
-
is the currently displayed page.
-
is the number of page numbers to display in the widget.
-
is the total number of pages.
-
is the ceiling function.
-
is the floor function.
-
is the maximum of
and
.
-
is the minimum of
and
.
That’s the short version. Now how do we derive that?
First, a warning. Don’t use page numbers as primitive objects unless
you’ll only have one item per page. If you’re going to list multiple
items per page, tracking page numbers instead of the first item to
display on a page will make your code unbelievably complicated when you
have to change the number of items displayed per page, or when you want
to show a list of items beginning with a specific index, rather than a
particular page.
So label your items from 0 to
,
and let
be the index of the first item to be displayed on the current page. If
each page is to show at most
entries (“at most” because there may not be enough items to fill the
last page to exactly
),
then
,
and the
th
page will start at
.
There will be
pages in all.
Now let’s move on to deriving the expressions for the actual
pagination range. What, precisely, is the problem? For a particular
integer between
and
,
we want to find a range of numbers of a fixed size which also fall in
and are as centered as possible around
.
As usual, we’ll describe the range as a half open interval by the first
page in the range
and one after the last page in the range
.
For example, in “« 4 5 6 7 »”,
and
.
Let the size of the desired range be
.
,
but we need a second constraint to center
as much as possible in this range. If
is odd, then we want the same number of entries on both sides of the
current page number. For example, if we are on page 3 and
,
We should display “« 1 2 3 4 5 »”. If
is even, we must make a choice: do we want to display more page numbers
after the current one, or more before? I chose to show more numbers
after on the theory that a user is more interested in stuff he has not
yet reached. This constraint translates to
So, centering the current page in the control,
should be one less than
since
is the first element and
is one after the last element. So if
isn odd,
.
If
is odd, since I chose to show more following pages, it is
.
We can combine all these expressions to get
which we’ll solve for
and
.
We’ll be using three properties of the floor and ceiling functions in
the calculations below, which I’ll summarize here:
We calculate thus:
{ solve first equation for
and second equation for
}
{ substitute
for
in first equation }
{ group
and simplify first equation }
{
}
{ substitute for
in second equation }
{
}
This works in the middle of the range. At the edges, we have to
calculate a different window. The first page is 0 and can never be less.
The last page
and can never be more. At the left end,
and
.
At the right end,
and
.
We augment the expressions for
and
to handle these cases (note that the order of operations is
important):
For quick reference, here is a Python function implementing this:
from math import ceil, floor
def bounds(current_page, n_pages, window):
return (
max(min(i - ceil(window / 2.0) + 1, npages - window), 0),
min(max(i + floor(window / 2.0), window), npages-1)
)
« Back to Miscellany | Home