Алгоритм / псевдокод для создания ссылок подкачки?
Вопрос
Может ли кто-нибудь предоставить код или псевдокод для того, как генерируются ссылки подкачки в StackOverflow?
Я продолжаю ломать голову, но не могу придумать достойный способ создания динамических ссылок, которые всегда показывают 2 страницы вокруг текущей, плюс первую и последнюю.
Пример: 1 ... 5 6 7 ... 593
Решение
Уже есть несколько других ответов, но я хотел бы показать вам подход, который я использовал для решения этой проблемы:Во-первых, давайте проверим, как Stack Overflow обрабатывает обычные случаи и граничные случаи.Каждая из моих страниц отображает 10 результатов, поэтому, чтобы узнать, что она делает для 1 страницы, найдите тег, содержащий менее 11 записей: удобство использования работает и сегодня.Мы видим, что ничего не отображается, что имеет смысл.
Как насчет 2 страниц?Найдите тег, содержащий от 11 до 20 записей (emacs работает сегодня).Мы видим:"1 2 Следующий" или "Предыдущий 1 2", в зависимости от того, на какой странице мы находимся.
3 страницы?"1 2 3 ...3 Следующий", "Предыдущий 1 2 3 Далее", и "Предыдущая статья 1 ...2 3".Интересно, что мы можем видеть, что само по себе Stack Overflow не очень хорошо справляется с этим граничным случаем:он должен отображать "1 2 ...3 Следующий"
4 страницы?"1 2 3 ...4 Далее", "Предыдущая статья 1 2 3 ...4 Далее", "Предыдущая статья 1 ...2 3 4 Далее" и "Предыдущая статья 1 ...3 4"
Наконец, давайте рассмотрим общий случай, N страниц:"1 2 3 ...N Следующий", "Предыдущая статья 1 2 3 ...Следующий", "Предыдущая статья 1 ...2 3 4 ...Следующий", "Предыдущая статья 1 ...3 4 5 ...Следующий" и т.д.
Давайте сделаем обобщение, основываясь на том, что мы видели:Похоже, у алгоритма есть эти общие черты:
- Если мы не на первой странице, выведите ссылку на предыдущую
- Всегда показывайте номер первой страницы
- Всегда отображайте номер текущей страницы
- Всегда отображайте страницу перед этой страницей и страницу после этой страницы.
- Всегда отображайте номер последней страницы
- Если мы не на последней странице, отобразите ссылку на следующую
Давайте проигнорируем граничный случай одной страницы и сделаем хорошую первую попытку использования алгоритма:(Как уже упоминалось, код для фактической печати ссылок был бы более сложным.Представьте, что в каждом месте мы размещаем номер страницы, предыдущую или Следующую в виде вызова функции, которая вернет правильный URL.)
function printPageLinksFirstTry(num totalPages, num currentPage)
if ( currentPage > 1 )
print "Prev"
print "1"
print "..."
print currentPage - 1
print currentPage
print currentPage + 1
print "..."
print totalPages
if ( currentPage < totalPages )
print "Next"
endFunction
Эта функция работает нормально, но она не учитывает, находимся ли мы рядом с первой или последней страницей.Рассматривая приведенные выше примеры, мы хотим отобразить только ...если текущая страница находится на расстоянии двух или более страниц.
function printPageLinksHandleCloseToEnds(num totalPages, num currentPage)
if ( currentPage > 1 )
print "Prev"
print "1"
if ( currentPage > 2 )
print "..."
if ( currentPage > 2 )
print currentPage - 1
print currentPage
if ( currentPage < totalPages - 1 )
print currentPage + 1
if ( currentPage < totalPages - 1 )
print "..."
print totalPages
if ( currentPage < totalPages )
print "Next"
endFunction
Как вы можете видеть, здесь у нас есть некоторое дублирование.Мы можем пойти дальше и почистить это для удобства чтения:
function printPageLinksCleanedUp(num totalPages, num currentPage)
if ( currentPage > 1 )
print "Prev"
print "1"
if ( currentPage > 2 )
print "..."
print currentPage - 1
print currentPage
if ( currentPage < totalPages - 1 )
print currentPage + 1
print "..."
print totalPages
if ( currentPage < totalPages )
print "Next"
endFunction
Осталось только две проблемы.Во-первых, мы неправильно печатаем для одной страницы, а во-вторых, мы будем печатать "1" дважды, если мы находимся на первой или последней странице.Давайте уберем их обоих за один раз:
function printPageLinksFinal(num totalPages, num currentPage)
if ( totalPages == 1 )
return
if ( currentPage > 1 )
print "Prev"
print "1"
if ( currentPage > 2 )
print "..."
print currentPage - 1
if ( currentPage != 1 and currentPage != totalPages )
print currentPage
if ( currentPage < totalPages - 1 )
print currentPage + 1
print "..."
print totalPages
if ( currentPage < totalPages )
print "Next"
endFunction
На самом деле, я солгал:У нас есть еще одна проблема.Когда у вас есть по крайней мере 4 страницы и вы находитесь на первой или последней странице, вы получаете дополнительную страницу на дисплее.Вместо того , чтобы "1 2 ...10 Далее " вы получаете "1 2 3 ...10 Следующий".Чтобы точно соответствовать тому, что происходит при переполнении стека, вам нужно будет проверить эту ситуацию:
function printPageLinksFinalReally(num totalPages, num currentPage)
if ( totalPages == 1 )
return
if ( currentPage > 1 )
print "Prev"
print "1"
if ( currentPage > 2 )
print "..."
if ( currentPage == totalPages and totalPages > 3 )
print currentPage - 2
print currentPage - 1
if ( currentPage != 1 and currentPage != totalPages )
print currentPage
if ( currentPage < totalPages - 1 )
print currentPage + 1
if ( currentPage == 1 and totalPages > 3 )
print currentPage + 2
print "..."
print totalPages
if ( currentPage < totalPages )
print "Next"
endFunction
Я надеюсь, что это поможет!
Другие советы
Элементы управления обычно показывают элементы управления для:P1, Pn, ПК (текущая страница), ПК+1, ПК-1.Единственный раз, когда это изменяется, находится на обоих концах диапазона подкачки {ПК < P3 или Pc > (Pn-3)}
- Первый шаг - это, очевидно, определить количество страниц:
numPages = ceiling(totalRecords / numPerPage)
Если у вас их 4 или меньше, на этом этапе отбрасывайте, потому что, согласно приведенным выше правилам, подкачка всегда будет фиксированной (P1, P2, Pn-1, Pn), где фактически будет Pc
в противном случае у вас есть три "состояния".
a.(ПК < P3) - итак, покажите P1, P2, P3, Pn, Затем, если Pc>1, покажите предыдущую ссылку перед P1.
b.(Pc> Pn - 2), поэтому покажите Предыдущие, P1, Pn - 2, Pn -1, Pn, покажите Следующую ссылку, если Pc < Pn
c.Показать предыдущее, P1, Pc -1, Pc, Pc +1, Pn, Далее
Проще простого в псевдокоде.Циклы могут стать немного неприятными при реализации, поскольку вам нужно выполнить некоторую итерацию, чтобы сгенерировать ссылки.
Редактировать:Конечно, предыдущие и Следующие идентичны Pc +/- 1
Что ж, если вы знаете текущую страницу, довольно тривиально просто вычесть число на 1 и добавить его на 1, затем сверить эти числа с границами и всегда отображать первую и последнюю страницу, затем, если они не расположены последовательно, добавить многоточия.
Или вы спрашиваете о получении общего количества страниц и определении текущего номера страницы ...?
public void PageLinks(int currentPage, int lastPage) {
if (currentPage > 2)
Add('[1]', '...');
for(int i=Math.Max(1, currentPage-1); i< Math.Min(currentPage+1, lastPage); i++)
Add('[i]');
if (currentPage < lastPage-1)
Add('...', '[lastpage]');
}
Последняя страница рассчитывается математически.Потолок (TotalRecords /recordsPerPage).
хммм.на самом деле, в случае, если currentpage равен 3, он все еще показывает [1]...[2][3][4]...[ xxx] я думаю, что многоточия в этом случае излишни.Но именно так это и работает.
Редактировать:предварительный просмотр правильно форматирует кодовый блок, почему он искажается?конечно, это просто псевдокод....но все же....
Это мой подход к созданию ссылки для подкачки.Следующее java- код это всего лишь псевдо.
package com.edde;
/**
* @author Yang Shuai
*/
public class Pager {
/**
* This is a method used to display the paging links(pagination or sometimes called pager).
* The totalPages are the total page you need to display. You can get this value using the
* formula:
*
* total_pages = total_records / items_per_page
*
* This methods is just a pseudo-code.
*
*
* @param totalPages how many pages you need to display
* @param currentPage you are in which page now
*/
public static void printPageLinks(int totalPages, int currentPage) {
// how many pages to display before and after the current page
int x = 2;
// if we just have one page, show nothing
if (totalPages == 1) {
return;
}
// if we are not at the first page, show the "Prev" button
if (currentPage > 1) {
System.out.print("Prev");
}
// always display the first page
if (currentPage == 1) {
System.out.print(" [1]");
} else {
System.out.print(" 1");
}
// besides the first and last page, how many pages do we need to display?
int how_many_times = 2 * x + 1;
// we use the left and right to restrict the range that we need to display
int left = Math.max(2, currentPage - 2 * x - 1);
int right = Math.min(totalPages - 1, currentPage + 2 * x + 1);
// the upper range restricted by left and right are more loosely than we need,
// so we further restrict this range we need to display
while (right - left > 2 * x) {
if (currentPage - left < right - currentPage) {
right--;
right = right < currentPage ? currentPage : right;
} else {
left++;
left = left > currentPage ? currentPage : left;
}
}
// do we need display the left "..."
if (left >= 3) {
System.out.print(" ...");
}
// now display the middle pages, we display how_many_times pages from page left
for (int i = 1, out = left; i <= how_many_times; i++, out++) {
// there are some pages we need not to display
if (out > right) {
continue;
}
// display the actual page
if (out == currentPage) {
System.out.print(" [" + out + "]");
} else {
System.out.print(" " + out);
}
}
// do we need the right "..."
if (totalPages - right >= 2) {
System.out.print(" ...");
}
// always display the last page
if (currentPage == totalPages) {
System.out.print(" [" + totalPages + "]");
} else {
System.out.print(" " + totalPages);
}
// if we are not at the last page, then display the "Next" button
if (currentPage < totalPages) {
System.out.print(" Next");
}
System.out.println();
}
public static void main(String[] args) {
// printPageLinks(50, 3);
help(500);
}
public static void test(int n) {
for (int i = 1; i <= n; i++) {
printPageLinks(n, i);
}
System.out.println("------------------------------");
}
public static void help(int n) {
for (int i = 1; i <= n; i++) {
test(i);
}
}
public static void help(int from, int to) {
for (int i = from; i <= to; i++) {
test(i);
}
}
}
Вот мой алгоритм, он работает действительно хорошо:
// Input
total_items // Number of rows, records etc. from db, file or whatever
per_page // num items per page
page // current page
visible_pages // number of visible pages
// Calculations
lastPage = ceil(total_items / per_page);
prevPage = page - 1 < 1 ? 0 : page - 1;
nextPage = page + 1 > lastPage ? 0 : page + 1;
halfpages = ceil(visible_pages / 2);
startPage = page - halfpages < 1 ? 1 : page - halfpages;
endPage = startPage + visible_pages - 1;
if(endPage > lastPage) {
startPage -= endPage - lastPage;
startPage = startPage < 1 ? 1 : startPage;
endPage = startPage + visible_pages > lastPage ? lastPage : startPage + visible_pages - 1;
}
// Output
lastPage // Total number of pages
prevPage // Previous page number (if 0 there is no prev page)
nextPage // Next page number (if 0 there is no next page)
startPage // First visible page
endPage // Last visible page
Итак, вы можете сделать такой пейджер, как этот:
if prevPage
[1] [prevPage]
endif
[startPage] ... [endPage]
if nextPage
[nextPage] [lastPage]
endif
или настройте все, что вам нравится.