Algorytm / pseudo-kod do tworzenia linków przywoławczych?

Czy someome może dostarczyć kod lub pseudo-kod do generowania linków stronicowania w StackOverflow?

Ciągle rozpracowuję swój mózg, ale nie mogę wymyślić przyzwoitego sposobu na zbudowanie dynamicznych linków, które zawsze pokazują 2 strony wokół bieżącego, plus pierwszą i ostatnią.

Przykład: 1 ... 5 6 7 ... 593

Author: Geoff, 2008-09-04

6 answers

Jest już kilka innych odpowiedzi, ale chciałbym pokazać, jakie podejście zastosowałem, aby je rozwiązać: Po pierwsze, sprawdźmy, jak Stack Overflow obsługuje zwykłe przypadki i przypadki krawędzi. Każda z moich stron wyświetla 10 wyników, więc aby dowiedzieć się, co robi dla 1 strony, znajdź tag, który ma mniej niż 11 wpisów: usability działa dzisiaj. Widzimy, że nic nie jest wyświetlane, co ma sens.

Może 2 strony? Znajdź tag, który zawiera od 11 do 20 wpisów (Emacs działa dzisiaj). Widzimy: "1 2 Next " lub " Prev 1 2", zależy, na której stronie jesteśmy.

3 strony? "1 2 3 ... 3 Next", "Prev 1 2 3 Next", oraz " Prev 1 ... 2 3". Co ciekawe, widzimy, że sam Stack Overflow nie radzi sobie z tym przypadkiem krawędzi: powinien wyświetlać "1 2 ... 3 Następna "

4 strony? "1 2 3 ... 4 Next", " Prev 1 2 3 ... 4 Next", " Prev 1 ... 2 3 4 Next " i " Prev 1 ... 3 4"

Wreszcie spójrzmy na ogólny przypadek, N stron: "1 2 3 ... N Next", " Prev 1 2 3 ... N Next", " Prev 1 ... 2 3 4 ... N Next", " Prev 1 ... 3 4 5 ... N Next", itp.

Uogólnijmy na podstawie tego, co widzieliśmy: Algorytm wydaje się mieć te cechy wspólne:

  • Jeśli nie jesteśmy na pierwszej stronie, wyświetl link do Prev
  • zawsze wyświetl numer pierwszej strony
  • Zawsze wyświetlaj aktualny numer strony
  • zawsze wyświetla stronę przed tą stroną i stronę po tej stronie.
  • zawsze wyświetl numer ostatniej strony
  • Jeśli nie jesteśmy na ostatniej stronie, wyświetl link do następnej

Zignorujmy Rozmiar Krawędzi pojedynczej strony i podejmijmy dobrą pierwszą próbę algorytmu: (jak już wspomniano, kod do wydrukowania linków byłby bardziej skomplikowany. Wyobraź sobie każde miejsce umieszczamy numer strony, Prev lub Next jako wywołanie funkcji zwróci poprawny adres 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

Ta funkcja Działa ok, ale nie bierze pod uwagę, czy jesteśmy w pobliżu pierwszej czy ostatniej strony. Patrząc na powyższe przykłady, chcemy tylko wyświetlić ... jeśli bieżąca strona znajduje się dwa lub więcej.

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

Jak widzisz, mamy tu pewne dublowanie. Możemy śmiało to wyczyścić dla czytelności:

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
Zostały tylko dwa problemy. Po pierwsze, nie drukujemy poprawnie jednej strony, a po drugie, będziemy wydrukuj" 1 " dwa razy, jeśli jesteśmy na pierwszej lub ostatniej stronie. Oczyśćmy je za jednym razem:
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
Właściwie, skłamałem: mamy jeszcze jeden problem. Jeśli masz co najmniej 4 strony i jesteś na pierwszej lub ostatniej stronie, otrzymasz dodatkową stronę na ekranie. Zamiast "1 2 ... 10 Następna""1 2 3 ... 10 Następna" Aby dokładnie dopasować to, co dzieje się w Stack Overflow, musisz sprawdzić tę sytuację:
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
Mam nadzieję, że to pomoże!
 46
Author: Bill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:53

Kontrolki zazwyczaj pokazują kontrolki dla: P1, Pn, Pc( bieżąca strona), Pc+1, Pc-1. Zmiana ta następuje tylko na obu końcach zakresu stronicowania {Pc (Pn-3)}

  • pierwszym krokiem jest oczywiście obliczenie liczby stron:

numPages = ceiling(totalRecords / numPerPage)

  • Jeśli masz 4 lub mniej, drop out w tym momencie, ponieważ, zgodnie z powyższymi zasadami, stronicowanie zawsze będzie stałe (P1, P2, Pn-1, Pn), gdzie jeden będzie acutally Pc

  • Else, masz trzy "stany"

A. (Pc 1, Pokaż link 'prev' przed P1.

B. (Pc > Pn - 2), więc pokaż Prev, P1, Pn-2, Pn -1, PN, Pokaż następny link, jeśli Pc

C. Pokaż Prev, P1, Pc -1, Pc, Pc +1, PN, Next

Proste jak Pie w pseudo kodzie. Pętle mogą być nieco nieprzyjemne po zaimplementowaniu, ponieważ musisz wykonać kilka iteracji, aby wygenerować linki.

Edytuj: Oczywiście Prev i Next są identyczne jak Pc +/- 1

 2
Author: Ian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-04 20:15:45

Cóż, jeśli znasz bieżącą stronę, to dość trywialne jest po prostu odjąć liczbę przez 1 i dodać ją przez 1, a następnie sprawdzić te liczby względem granic i wyświetlać zawsze pierwszą i ostatnią stronę, a następnie, jeśli nie są w kolejności, dodaj elipsy.

A może pytasz o uzyskanie całkowitej liczby stron i określenie bieżącego numeru strony...?

 1
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-04 19:26:41
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]');
}

LastPage jest obliczany jako matematyka.Sufit(totalRecords/Recordspepage).

Hmmm. w rzeczywistości, w przypadku, gdy currentpage jest 3, nadal pokazuje [1]...[2][3][4]...[xxx] myślę, że elipsy są w tym przypadku zbędne. Ale tak to działa.

Edit: podgląd formatuje kod poprawnie, dlaczego jest zniekształcony? jasne, to tylko pseudokod.... ale jednak....

 1
Author: Jimmy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-04 20:02:54

Oto moje podejście do tworzenia łącza przywoławczego. Następujące kod java to tylko pseudo.

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);
        }
    }

}
 0
Author: Just a learner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-17 07:30:03

Oto mój algorytm. Działa naprawdę ładnie:

// 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

Więc możesz zrobić pager taki:

if prevPage
    [1] [prevPage] 
endif

[startPage] ... [endPage] 

if nextPage
    [nextPage] [lastPage] 
endif
Albo dostosuj, co chcesz.
 0
Author: Tsvetan Filev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-19 14:17:52