Generating pagination links











up vote
5
down vote

favorite
2












I recently had an interview for a programming job and after the interview I was then asked to create a function with the following brief:




The Task:



Relating to pagination links: given a page number, a total number of pages, and an amount of 'context' pages, generate the appropriate pagination links. For example, with 9 pages in total and a context of 1 page, you would get the following results for pages 1 to 9:



For page 1: 1 2 ... 8 9



For page 2: 1 2 3 ... 8 9



For page 3: 1 2 3 4 ... 8 9



For page 4: 1 2 3 4 5 ... 8 9



For page 5: 1 2 ... 4 5 6 ... 8 9



For page 6: 1 2 ... 5 6 7 8 9



For page 7: 1 2 ... 6 7 8 9



For page 8: 1 2 ... 7 8 9



For page 9: 1 2 ... 8 9



I would like you to write a PHP function with the following signature to solve this task:



function outputLinks($page, $numberOfPages, $context)



The code below is what I submitted and I've now been asked to complete another task which is good news in my eyes, so I can only assume that my code for this task was at least good enough. But I was just wondering how good my submission really was! Is there an easier (or simpler) way to achieve the same results? Could I shorten my code at all?



<?php

// Global Variables

$p = $_GET['p']; // Current Page
$t = 9; // Total Pages
$c = 1; // Context Pages

// Pagination Function

function outputLinks($page, $numberOfPages, $context) {

// Correct page variable

if (!$page || $p < 1) { $page = 1; }
if ($page > $numberOfPages) { $page = $numberOfPages; }

// Set array variables

$leftArray = $midArray = $rightArray = array();

// Left Array

for ($x = 1; $x <= (1 + $context) && $x <= $numberOfPages; $x++) {
array_push($leftArray, $x);
}

// Right Array

for ($x = $numberOfPages - $context; $x <= $numberOfPages && $x > 0; $x++) {
if (!in_array($x, $leftArray)) {
array_push($rightArray, $x);
}
}

// Left/Right Array First/Last values to variables

$firstInLeftArray = current($leftArray);
$lastInLeftArray = end($leftArray);
$firstInRightArray = current($rightArray);
$lastInRightArray = end($rightArray);

// Middle Array

if ($page == $firstInLeftArray || $page == $lastInRightArray) {

if (($firstInRightArray - $lastInLeftArray) > 1) {
array_push($midArray, ' ... ');
}

} else {

if (in_array($page, $leftArray)) {
for ($x = $page; $x <= ($page + $context) && $x <= $numberOfPages; $x++) {
if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
array_push($midArray, $x);
}
}
} else if (in_array($page, $rightArray)) {
for ($x = ($page - $context); $x < $firstInRightArray && $x > $lastInLeftArray; $x++) {
array_push($midArray, $x);
}
} else {
for ($x = ($page - $context); $x <= ($page + $context) && $x > 0; $x++) {
if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
array_push($midArray, $x);
}
}
}

// Middle Array First/Last values to variables

$firstInmidArray = current($midArray);
$lastInmidArray = end($midArray);

// Ellipses for omitted numbers

if (($firstInmidArray - $lastInLeftArray) > 1) {
array_push($leftArray, ' ... ');
}

if (!empty($midArray) && ($firstInRightArray - $lastInmidArray) > 1) {
array_push($midArray, ' ... ');
}

}

// Display Arrays

echo 'Page '.$page.' of '.$numberOfPages.': ';

foreach ($leftArray as $value) {
if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
}

foreach ($midArray as $value) {
if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
}

foreach ($rightArray as $value) {
echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> ';
}

}

// Initialize document

print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

// Generate Pagination through the outputLinks function

outputLinks($p, $t, $c);

// Close the document

print('</body></html>');

?>









share|improve this question




























    up vote
    5
    down vote

    favorite
    2












    I recently had an interview for a programming job and after the interview I was then asked to create a function with the following brief:




    The Task:



    Relating to pagination links: given a page number, a total number of pages, and an amount of 'context' pages, generate the appropriate pagination links. For example, with 9 pages in total and a context of 1 page, you would get the following results for pages 1 to 9:



    For page 1: 1 2 ... 8 9



    For page 2: 1 2 3 ... 8 9



    For page 3: 1 2 3 4 ... 8 9



    For page 4: 1 2 3 4 5 ... 8 9



    For page 5: 1 2 ... 4 5 6 ... 8 9



    For page 6: 1 2 ... 5 6 7 8 9



    For page 7: 1 2 ... 6 7 8 9



    For page 8: 1 2 ... 7 8 9



    For page 9: 1 2 ... 8 9



    I would like you to write a PHP function with the following signature to solve this task:



    function outputLinks($page, $numberOfPages, $context)



    The code below is what I submitted and I've now been asked to complete another task which is good news in my eyes, so I can only assume that my code for this task was at least good enough. But I was just wondering how good my submission really was! Is there an easier (or simpler) way to achieve the same results? Could I shorten my code at all?



    <?php

    // Global Variables

    $p = $_GET['p']; // Current Page
    $t = 9; // Total Pages
    $c = 1; // Context Pages

    // Pagination Function

    function outputLinks($page, $numberOfPages, $context) {

    // Correct page variable

    if (!$page || $p < 1) { $page = 1; }
    if ($page > $numberOfPages) { $page = $numberOfPages; }

    // Set array variables

    $leftArray = $midArray = $rightArray = array();

    // Left Array

    for ($x = 1; $x <= (1 + $context) && $x <= $numberOfPages; $x++) {
    array_push($leftArray, $x);
    }

    // Right Array

    for ($x = $numberOfPages - $context; $x <= $numberOfPages && $x > 0; $x++) {
    if (!in_array($x, $leftArray)) {
    array_push($rightArray, $x);
    }
    }

    // Left/Right Array First/Last values to variables

    $firstInLeftArray = current($leftArray);
    $lastInLeftArray = end($leftArray);
    $firstInRightArray = current($rightArray);
    $lastInRightArray = end($rightArray);

    // Middle Array

    if ($page == $firstInLeftArray || $page == $lastInRightArray) {

    if (($firstInRightArray - $lastInLeftArray) > 1) {
    array_push($midArray, ' ... ');
    }

    } else {

    if (in_array($page, $leftArray)) {
    for ($x = $page; $x <= ($page + $context) && $x <= $numberOfPages; $x++) {
    if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
    array_push($midArray, $x);
    }
    }
    } else if (in_array($page, $rightArray)) {
    for ($x = ($page - $context); $x < $firstInRightArray && $x > $lastInLeftArray; $x++) {
    array_push($midArray, $x);
    }
    } else {
    for ($x = ($page - $context); $x <= ($page + $context) && $x > 0; $x++) {
    if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
    array_push($midArray, $x);
    }
    }
    }

    // Middle Array First/Last values to variables

    $firstInmidArray = current($midArray);
    $lastInmidArray = end($midArray);

    // Ellipses for omitted numbers

    if (($firstInmidArray - $lastInLeftArray) > 1) {
    array_push($leftArray, ' ... ');
    }

    if (!empty($midArray) && ($firstInRightArray - $lastInmidArray) > 1) {
    array_push($midArray, ' ... ');
    }

    }

    // Display Arrays

    echo 'Page '.$page.' of '.$numberOfPages.': ';

    foreach ($leftArray as $value) {
    if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
    }

    foreach ($midArray as $value) {
    if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
    }

    foreach ($rightArray as $value) {
    echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> ';
    }

    }

    // Initialize document

    print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

    // Generate Pagination through the outputLinks function

    outputLinks($p, $t, $c);

    // Close the document

    print('</body></html>');

    ?>









    share|improve this question


























      up vote
      5
      down vote

      favorite
      2









      up vote
      5
      down vote

      favorite
      2






      2





      I recently had an interview for a programming job and after the interview I was then asked to create a function with the following brief:




      The Task:



      Relating to pagination links: given a page number, a total number of pages, and an amount of 'context' pages, generate the appropriate pagination links. For example, with 9 pages in total and a context of 1 page, you would get the following results for pages 1 to 9:



      For page 1: 1 2 ... 8 9



      For page 2: 1 2 3 ... 8 9



      For page 3: 1 2 3 4 ... 8 9



      For page 4: 1 2 3 4 5 ... 8 9



      For page 5: 1 2 ... 4 5 6 ... 8 9



      For page 6: 1 2 ... 5 6 7 8 9



      For page 7: 1 2 ... 6 7 8 9



      For page 8: 1 2 ... 7 8 9



      For page 9: 1 2 ... 8 9



      I would like you to write a PHP function with the following signature to solve this task:



      function outputLinks($page, $numberOfPages, $context)



      The code below is what I submitted and I've now been asked to complete another task which is good news in my eyes, so I can only assume that my code for this task was at least good enough. But I was just wondering how good my submission really was! Is there an easier (or simpler) way to achieve the same results? Could I shorten my code at all?



      <?php

      // Global Variables

      $p = $_GET['p']; // Current Page
      $t = 9; // Total Pages
      $c = 1; // Context Pages

      // Pagination Function

      function outputLinks($page, $numberOfPages, $context) {

      // Correct page variable

      if (!$page || $p < 1) { $page = 1; }
      if ($page > $numberOfPages) { $page = $numberOfPages; }

      // Set array variables

      $leftArray = $midArray = $rightArray = array();

      // Left Array

      for ($x = 1; $x <= (1 + $context) && $x <= $numberOfPages; $x++) {
      array_push($leftArray, $x);
      }

      // Right Array

      for ($x = $numberOfPages - $context; $x <= $numberOfPages && $x > 0; $x++) {
      if (!in_array($x, $leftArray)) {
      array_push($rightArray, $x);
      }
      }

      // Left/Right Array First/Last values to variables

      $firstInLeftArray = current($leftArray);
      $lastInLeftArray = end($leftArray);
      $firstInRightArray = current($rightArray);
      $lastInRightArray = end($rightArray);

      // Middle Array

      if ($page == $firstInLeftArray || $page == $lastInRightArray) {

      if (($firstInRightArray - $lastInLeftArray) > 1) {
      array_push($midArray, ' ... ');
      }

      } else {

      if (in_array($page, $leftArray)) {
      for ($x = $page; $x <= ($page + $context) && $x <= $numberOfPages; $x++) {
      if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
      array_push($midArray, $x);
      }
      }
      } else if (in_array($page, $rightArray)) {
      for ($x = ($page - $context); $x < $firstInRightArray && $x > $lastInLeftArray; $x++) {
      array_push($midArray, $x);
      }
      } else {
      for ($x = ($page - $context); $x <= ($page + $context) && $x > 0; $x++) {
      if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
      array_push($midArray, $x);
      }
      }
      }

      // Middle Array First/Last values to variables

      $firstInmidArray = current($midArray);
      $lastInmidArray = end($midArray);

      // Ellipses for omitted numbers

      if (($firstInmidArray - $lastInLeftArray) > 1) {
      array_push($leftArray, ' ... ');
      }

      if (!empty($midArray) && ($firstInRightArray - $lastInmidArray) > 1) {
      array_push($midArray, ' ... ');
      }

      }

      // Display Arrays

      echo 'Page '.$page.' of '.$numberOfPages.': ';

      foreach ($leftArray as $value) {
      if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
      }

      foreach ($midArray as $value) {
      if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
      }

      foreach ($rightArray as $value) {
      echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> ';
      }

      }

      // Initialize document

      print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

      // Generate Pagination through the outputLinks function

      outputLinks($p, $t, $c);

      // Close the document

      print('</body></html>');

      ?>









      share|improve this question















      I recently had an interview for a programming job and after the interview I was then asked to create a function with the following brief:




      The Task:



      Relating to pagination links: given a page number, a total number of pages, and an amount of 'context' pages, generate the appropriate pagination links. For example, with 9 pages in total and a context of 1 page, you would get the following results for pages 1 to 9:



      For page 1: 1 2 ... 8 9



      For page 2: 1 2 3 ... 8 9



      For page 3: 1 2 3 4 ... 8 9



      For page 4: 1 2 3 4 5 ... 8 9



      For page 5: 1 2 ... 4 5 6 ... 8 9



      For page 6: 1 2 ... 5 6 7 8 9



      For page 7: 1 2 ... 6 7 8 9



      For page 8: 1 2 ... 7 8 9



      For page 9: 1 2 ... 8 9



      I would like you to write a PHP function with the following signature to solve this task:



      function outputLinks($page, $numberOfPages, $context)



      The code below is what I submitted and I've now been asked to complete another task which is good news in my eyes, so I can only assume that my code for this task was at least good enough. But I was just wondering how good my submission really was! Is there an easier (or simpler) way to achieve the same results? Could I shorten my code at all?



      <?php

      // Global Variables

      $p = $_GET['p']; // Current Page
      $t = 9; // Total Pages
      $c = 1; // Context Pages

      // Pagination Function

      function outputLinks($page, $numberOfPages, $context) {

      // Correct page variable

      if (!$page || $p < 1) { $page = 1; }
      if ($page > $numberOfPages) { $page = $numberOfPages; }

      // Set array variables

      $leftArray = $midArray = $rightArray = array();

      // Left Array

      for ($x = 1; $x <= (1 + $context) && $x <= $numberOfPages; $x++) {
      array_push($leftArray, $x);
      }

      // Right Array

      for ($x = $numberOfPages - $context; $x <= $numberOfPages && $x > 0; $x++) {
      if (!in_array($x, $leftArray)) {
      array_push($rightArray, $x);
      }
      }

      // Left/Right Array First/Last values to variables

      $firstInLeftArray = current($leftArray);
      $lastInLeftArray = end($leftArray);
      $firstInRightArray = current($rightArray);
      $lastInRightArray = end($rightArray);

      // Middle Array

      if ($page == $firstInLeftArray || $page == $lastInRightArray) {

      if (($firstInRightArray - $lastInLeftArray) > 1) {
      array_push($midArray, ' ... ');
      }

      } else {

      if (in_array($page, $leftArray)) {
      for ($x = $page; $x <= ($page + $context) && $x <= $numberOfPages; $x++) {
      if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
      array_push($midArray, $x);
      }
      }
      } else if (in_array($page, $rightArray)) {
      for ($x = ($page - $context); $x < $firstInRightArray && $x > $lastInLeftArray; $x++) {
      array_push($midArray, $x);
      }
      } else {
      for ($x = ($page - $context); $x <= ($page + $context) && $x > 0; $x++) {
      if (!in_array($x, $leftArray) && !in_array($x, $rightArray)) {
      array_push($midArray, $x);
      }
      }
      }

      // Middle Array First/Last values to variables

      $firstInmidArray = current($midArray);
      $lastInmidArray = end($midArray);

      // Ellipses for omitted numbers

      if (($firstInmidArray - $lastInLeftArray) > 1) {
      array_push($leftArray, ' ... ');
      }

      if (!empty($midArray) && ($firstInRightArray - $lastInmidArray) > 1) {
      array_push($midArray, ' ... ');
      }

      }

      // Display Arrays

      echo 'Page '.$page.' of '.$numberOfPages.': ';

      foreach ($leftArray as $value) {
      if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
      }

      foreach ($midArray as $value) {
      if ($value == ' ... ') { echo $value; } else { echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> '; }
      }

      foreach ($rightArray as $value) {
      echo ' <a href="?p='.$value.'" target="_self">'.$value.'</a> ';
      }

      }

      // Initialize document

      print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

      // Generate Pagination through the outputLinks function

      outputLinks($p, $t, $c);

      // Close the document

      print('</body></html>');

      ?>






      php pagination






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 40 mins ago









      Jamal

      30.2k11115226




      30.2k11115226










      asked Mar 23 '12 at 4:11









      Joe

      182129




      182129






















          3 Answers
          3






          active

          oldest

          votes

















          up vote
          6
          down vote













          Yes, I would have written this completely differently (so I decided to do just that). I'll follow my solution with a hopefully scientific analysis of both solutions. Finally I'll give some personal recommendations.



          Note: The reason for the in-depth analysis is that I was already interested in pagination and thinking about an implementation for a framework that I am writing.



          Model



          /** Model the data for pagination.
          */
          class ModelPaginator
          {
          /** Get the links for pagination.
          * @param page int The page number for the current page.
          * @param numberOfPages int The total number of paginated pages.
          * @param context int The number of context pages surrounding the current,
          * first and last pages.
          * @return array An array with the ranges of pages for pagination.
          */
          public function getLinks($page, $numberOfPages, $context)
          {
          // Sanitize the inputs (I am trying to follow your example here, I would
          // throw exceptions if the values weren't as I expected).
          $numberOfPages = max($numberOfPages, 1);
          $context = min(max($context, 0), $numberOfPages - 1);
          $page = min(max($page, 1), $numberOfPages);

          return array_unique(
          array_merge(range(1, 1 + $context),
          range(max($page - $context, 1),
          min($page + $context, $numberOfPages)),
          range($numberOfPages - $context, $numberOfPages)));
          }
          }


          View



          /** A simple view for paginator links.
          */
          class ViewPaginatorLinks
          {
          protected $separator;

          /** Construct the paginator.
          * @param separator string Separator between gaps.
          */
          public function __construct($separator=' ... ')
          {
          $this->separator = $separator;
          }

          /** Write the pagination links.
          * @param links array The links array.
          * @param currentPage int The current page.
          */
          public function write($links, $currentPage)
          {
          $currentPage = min(max($currentPage, 1), end($links));

          echo 'Page ' . $currentPage . ' of ' . end($links) . ': ';
          $previousPage = 0;

          foreach ($links as $page) {
          if ($page !== $previousPage + 1) {
          $this->writeSeparator();
          }

          $this->writeLink($page);
          $previousPage = $page;
          }
          }

          /*******************/
          /* Private Methods */
          /*******************/

          /** Write the link to the paginated page.
          * @param page array The page that we are writing the link for.
          */
          private function writeLink($page)
          {
          echo '<a href="?p=' . $page . '" target="_self">' . $page . '</a>' .
          "n";
          }

          /// Write the separator that bridges gaps in the pagination.
          private function writeSeparator()
          {
          echo '<span>' . $this->separator . '</span>' . "n";
          }
          }


          Usage



          $numberOfPages = 29;
          $currentPage = 13;
          $context = 1;

          $Model = new ModelPaginator;
          $View = new ViewPaginatorLinks;

          $View->write($Model->getLinks($currentPage, $numberOfPages, $context),
          $currentPage);


          Analysis



          I'll be going through some metrics thanks to PHPLOC. I added the maximum line length to these metrics.



          Your Solution



          Lines of Code (LOC):                                117
          Cyclomatic Complexity / Lines of Code: 0.37
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 83

          Maximum Line Length 120


          My Solution



          Lines of Code (LOC):                                 97
          Cyclomatic Complexity / Lines of Code: 0.03
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 63

          Maximum Line Length 80


          Lines of Code



          My solution has 20 fewer lines. This could well be due to removing quite a few blank lines. However, measured in characters it is 2905 characters long against your 4089. So there is definitely a large difference in typing required.



          Cyclomatic Complexity



          This is a measure of how complex the code is (see wikipedia). Highly complex code is normally harder to maintain and contains more bugs. My code is a factor of 10 less complex. This is due to the flatness of my code. See Jeff Atwood's Flattening Arrow Code blog.



          Comments



          The number has remained the same according to PHPLOC, but it seems to be counting blank lines. Manually I can only count 14 in your code versus 27 in mine, even though the number of lines of code were reduced in my solution. Proportionally the commenting in my solution is much higher. At the highest level of abstraction (class, function definitions) I have comments where you have virtually none. My comments cover input parameters to functions and can be used in automatic documentation tools like Doxygen or PHPDocumentor.



          Line Length



          Firstly, I am unsure whether the formatting of your code here is exactly as it was. I'll assume the true maximum line length of your code is 120 characters. These days monitors can handle 120 characters wide. However when you are editing two or three files side by side they can't. You also end up with large blank areas for showing the long lines (unless you are ok with it wrapping).



          Performance



          I ran each code 100, 000 times with no output being echoed (the buffer was cleared after each iteration).



          There were a few different configurations that I tested as far as object creation and usage:




          • Creating objects within each loop: Your code was 21% faster.

          • Creating the objects outside of the loop: Your code was 10% faster.

          • Creating the objects outside the loop, getting the model data and running only the view code in the loop: Mine was 27% faster.


          Your code has the better performance, but the 100, 000 iterations only took 5.8 4.2 seconds (see edit below) on my not very fast machine. I think the performance difference is probably due to my split of Model and View. I would only consider changing this split if the code was running on a site with enormous traffic and it was shown by profiling that real gains could be had from changing the pagination.



          Edit: The performance gains from your solution were troubling me. With some profiling I was able to see that the View was wasting a lot of time dispatching the calls with $this->writeSeparator and $this->writeLink. Replacing these calls with their code (seeing as they were only 1 line anyway) led to a large performance gain. My code was faster by 10%, 18% and 59%. Worst case performance is 4.2s for 100 000 iterations (context = 1).



          Real Recommendations



          The following line should not have $p in it. Globals are bad practice and will only make you look bad.



          if (!$page || $p < 1) { $page = 1; }



          Comments belong with the code they are commenting on. A blank line after a comment only distances it from that.



          I think your code was missing a '}' for the end of your function.



          Mixed indentation for if/elseif removes the visual cues for grouping. Instead of:



          if () {
          } else if () {
          }


          Use:



          if () {
          }
          elseif () { // elseif is equivalent to else if
          }


          Similarly I would avoid one line if and if/else statements






          share|improve this answer























          • Cool! But not enough checks! :) What if $context == $numberOfPages in the Model? What if $currentPage > $numberOfPages in the View?
            – Michael
            Mar 23 '12 at 18:06










          • Thanks for that, I've fixed those now and added some analysis.
            – Paul
            Mar 24 '12 at 2:46










          • That is one heck of a reply, thank you!! I think your coding knowledge is above mine ha! I've never used model/classes if I'm honest so that's a little new to me but I'm taking it all in as well as your recommendations! However, I've copied your code (model, view, usage - in that order) and tested it and it doesn't work so I'm assuming I'm probably doing something wrong, but I can't see what exactly. I only changed the $numberOfPages = 9; $currentPage = $_GET['p']; and if I go to page 1 I get the output: Page 1 of 9: 12...12...89 and going to page 2: Page 2 of 9: 12...123...89
            – Joe
            Mar 26 '12 at 2:14










          • Oh and as for missing the closing bracket on the function I think that was me just accidentally deleting it on this text editor, it was there in the real code :)
            – Joe
            Mar 26 '12 at 2:16










          • Sorry, I have fixed my solution now. I needed to use array_unique to remove the duplicate values. You did well with your solution, getting it right is the main thing.
            – Paul
            Mar 26 '12 at 3:59


















          up vote
          0
          down vote













          You can cut out some redundancy by just using one array to store the results, and you can cut down on the number of operations by doing simple checks against what we know will be the last and first members of the outer page groups instead of checking whether those numbers are in the right/left arrays. If you wanted to be super fancy, and save even more operations, you could store all those values in variables so you're not doing the arithmetic operations every iteration in the loops, but I didn't bother.



          I'm not too well versed in php, but here's a version with some changes. I was able to cut about 40 lines.



          <?php

          // Global Variables

          $p = $_GET['p']; // Current Page
          $t = 9; // Total Pages
          $c = 1; // Context Pages

          // Pagination Function
          function outputLinks($page, $numberOfPages, $context)
          {
          $display = array();

          //Left-side pages
          for($i = 1; $i <= 1 + $context; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          if(($page - $context) - (1 + $context) > 1)
          array_push($display, "...");

          //Middle pages
          for($i = ($page - $context); $i <= ($page + $context); $i++)
          {
          if($i > (1 + $context) && $i < ($numberOfPages - $context))
          array_push($display, buildPageLink($i));
          }

          if($page + $context < $numberOfPages - $context)
          array_push($display, "...");

          //Right-side pages
          for($i = $numberOfPages - $context; $i <= $numberOfPages; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          echo 'Page ' . $page . ' of ' .$numberOfPages . ': ';
          foreach($display as $val)
          {
          echo $val;
          }

          }

          function buildPageLink($pagenum)
          {
          return ' <a href="?p='.$pagenum.'" target="_self">'.$pagenum.'</a> ';
          }

          // Initialize document

          print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

          // Generate Pagination through the outputLinks function

          outputLinks($p, $t, $c);

          // Close the document

          print('</body></html>');

          ?>





          share|improve this answer





















          • That is definitely a better approach than mine but it doesn't work in all scenarios like mine does. If you changed numberOfPages from 9 to 3, you get Page 1 of 3: 1 2 2 3 and also if you have more context pages than numberOfPages you get problems, for example $numberOfPages = 9 and $context = 10 you get Page 9 of 9: 1 2 3 4 5 6 7 8 9 10 11 -1 0 1 2 3 4 5 6 7 8 9 - I know this can be fixed in your code too. But just wanted to point out it doesn't work in every case. Thank you for the ideas though!
            – Joe
            Mar 26 '12 at 1:57


















          up vote
          0
          down vote













          <?php
          function outputLinks( $page, $numberOfPages, $context ) {
          # find the context pages
          $pages = array_filter( range( $page - $context, $page + $context ), function( $value ) use ($numberOfPages) {
          if ( $value <= 2 || $value >= ( $numberOfPages - 1 ) ) return false;
          return true;
          });

          # are there any gaps to fill with '...':
          if ( isset( $pages[0] ) ) {
          if ( $pages[0] > 2 ) array_unshift( $pages, '...' );
          if ( $pages[ count( $pages ) - 1 ] < ( $numberOfPages - 2 ) ) array_push( $pages, '...' );
          }

          # add the first and last links:
          array_push( $pages, $numberOfPages - 1, $numberOfPages );
          array_unshift( $pages, 1, 2 );

          foreach ( $pages as $key => & $page ) {
          # if ( is_int( $page ) ) $page = '<a href="?p=' . $page . '" />';
          }

          return $pages;
          }
          # your example with my impl:
          for ( $i = 1; $i <= 9; $i++ ) echo implode(' ', outputLinks( $i, 9, 1 ), PHP_EOL;


          I had to give this one a shot aiming for the least amount of lines logic as possible, so here's my solution. It's split in 4 "blocks":
          - find the context pages
          - fill in blanks between the beginning/ending and the context pages with '...'
          - add the first/last pages
          - lastly, loop through the results ( uncomment line 20 ) and make them into links.



          I can't think of anything simpler, it's already down to 16 lines of code.



          Cheers






          share|improve this answer





















          • There are different but equally valid assumptions with the first and last pages which have a fixed context of 1 page in this solution. There is a small bug, it prints 1289 without '...' between the 2 and 8. Also a copy paste error on the final line with the ) and ,.
            – Paul
            Apr 6 '12 at 2:23











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f10271%2fgenerating-pagination-links%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          3 Answers
          3






          active

          oldest

          votes








          3 Answers
          3






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          6
          down vote













          Yes, I would have written this completely differently (so I decided to do just that). I'll follow my solution with a hopefully scientific analysis of both solutions. Finally I'll give some personal recommendations.



          Note: The reason for the in-depth analysis is that I was already interested in pagination and thinking about an implementation for a framework that I am writing.



          Model



          /** Model the data for pagination.
          */
          class ModelPaginator
          {
          /** Get the links for pagination.
          * @param page int The page number for the current page.
          * @param numberOfPages int The total number of paginated pages.
          * @param context int The number of context pages surrounding the current,
          * first and last pages.
          * @return array An array with the ranges of pages for pagination.
          */
          public function getLinks($page, $numberOfPages, $context)
          {
          // Sanitize the inputs (I am trying to follow your example here, I would
          // throw exceptions if the values weren't as I expected).
          $numberOfPages = max($numberOfPages, 1);
          $context = min(max($context, 0), $numberOfPages - 1);
          $page = min(max($page, 1), $numberOfPages);

          return array_unique(
          array_merge(range(1, 1 + $context),
          range(max($page - $context, 1),
          min($page + $context, $numberOfPages)),
          range($numberOfPages - $context, $numberOfPages)));
          }
          }


          View



          /** A simple view for paginator links.
          */
          class ViewPaginatorLinks
          {
          protected $separator;

          /** Construct the paginator.
          * @param separator string Separator between gaps.
          */
          public function __construct($separator=' ... ')
          {
          $this->separator = $separator;
          }

          /** Write the pagination links.
          * @param links array The links array.
          * @param currentPage int The current page.
          */
          public function write($links, $currentPage)
          {
          $currentPage = min(max($currentPage, 1), end($links));

          echo 'Page ' . $currentPage . ' of ' . end($links) . ': ';
          $previousPage = 0;

          foreach ($links as $page) {
          if ($page !== $previousPage + 1) {
          $this->writeSeparator();
          }

          $this->writeLink($page);
          $previousPage = $page;
          }
          }

          /*******************/
          /* Private Methods */
          /*******************/

          /** Write the link to the paginated page.
          * @param page array The page that we are writing the link for.
          */
          private function writeLink($page)
          {
          echo '<a href="?p=' . $page . '" target="_self">' . $page . '</a>' .
          "n";
          }

          /// Write the separator that bridges gaps in the pagination.
          private function writeSeparator()
          {
          echo '<span>' . $this->separator . '</span>' . "n";
          }
          }


          Usage



          $numberOfPages = 29;
          $currentPage = 13;
          $context = 1;

          $Model = new ModelPaginator;
          $View = new ViewPaginatorLinks;

          $View->write($Model->getLinks($currentPage, $numberOfPages, $context),
          $currentPage);


          Analysis



          I'll be going through some metrics thanks to PHPLOC. I added the maximum line length to these metrics.



          Your Solution



          Lines of Code (LOC):                                117
          Cyclomatic Complexity / Lines of Code: 0.37
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 83

          Maximum Line Length 120


          My Solution



          Lines of Code (LOC):                                 97
          Cyclomatic Complexity / Lines of Code: 0.03
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 63

          Maximum Line Length 80


          Lines of Code



          My solution has 20 fewer lines. This could well be due to removing quite a few blank lines. However, measured in characters it is 2905 characters long against your 4089. So there is definitely a large difference in typing required.



          Cyclomatic Complexity



          This is a measure of how complex the code is (see wikipedia). Highly complex code is normally harder to maintain and contains more bugs. My code is a factor of 10 less complex. This is due to the flatness of my code. See Jeff Atwood's Flattening Arrow Code blog.



          Comments



          The number has remained the same according to PHPLOC, but it seems to be counting blank lines. Manually I can only count 14 in your code versus 27 in mine, even though the number of lines of code were reduced in my solution. Proportionally the commenting in my solution is much higher. At the highest level of abstraction (class, function definitions) I have comments where you have virtually none. My comments cover input parameters to functions and can be used in automatic documentation tools like Doxygen or PHPDocumentor.



          Line Length



          Firstly, I am unsure whether the formatting of your code here is exactly as it was. I'll assume the true maximum line length of your code is 120 characters. These days monitors can handle 120 characters wide. However when you are editing two or three files side by side they can't. You also end up with large blank areas for showing the long lines (unless you are ok with it wrapping).



          Performance



          I ran each code 100, 000 times with no output being echoed (the buffer was cleared after each iteration).



          There were a few different configurations that I tested as far as object creation and usage:




          • Creating objects within each loop: Your code was 21% faster.

          • Creating the objects outside of the loop: Your code was 10% faster.

          • Creating the objects outside the loop, getting the model data and running only the view code in the loop: Mine was 27% faster.


          Your code has the better performance, but the 100, 000 iterations only took 5.8 4.2 seconds (see edit below) on my not very fast machine. I think the performance difference is probably due to my split of Model and View. I would only consider changing this split if the code was running on a site with enormous traffic and it was shown by profiling that real gains could be had from changing the pagination.



          Edit: The performance gains from your solution were troubling me. With some profiling I was able to see that the View was wasting a lot of time dispatching the calls with $this->writeSeparator and $this->writeLink. Replacing these calls with their code (seeing as they were only 1 line anyway) led to a large performance gain. My code was faster by 10%, 18% and 59%. Worst case performance is 4.2s for 100 000 iterations (context = 1).



          Real Recommendations



          The following line should not have $p in it. Globals are bad practice and will only make you look bad.



          if (!$page || $p < 1) { $page = 1; }



          Comments belong with the code they are commenting on. A blank line after a comment only distances it from that.



          I think your code was missing a '}' for the end of your function.



          Mixed indentation for if/elseif removes the visual cues for grouping. Instead of:



          if () {
          } else if () {
          }


          Use:



          if () {
          }
          elseif () { // elseif is equivalent to else if
          }


          Similarly I would avoid one line if and if/else statements






          share|improve this answer























          • Cool! But not enough checks! :) What if $context == $numberOfPages in the Model? What if $currentPage > $numberOfPages in the View?
            – Michael
            Mar 23 '12 at 18:06










          • Thanks for that, I've fixed those now and added some analysis.
            – Paul
            Mar 24 '12 at 2:46










          • That is one heck of a reply, thank you!! I think your coding knowledge is above mine ha! I've never used model/classes if I'm honest so that's a little new to me but I'm taking it all in as well as your recommendations! However, I've copied your code (model, view, usage - in that order) and tested it and it doesn't work so I'm assuming I'm probably doing something wrong, but I can't see what exactly. I only changed the $numberOfPages = 9; $currentPage = $_GET['p']; and if I go to page 1 I get the output: Page 1 of 9: 12...12...89 and going to page 2: Page 2 of 9: 12...123...89
            – Joe
            Mar 26 '12 at 2:14










          • Oh and as for missing the closing bracket on the function I think that was me just accidentally deleting it on this text editor, it was there in the real code :)
            – Joe
            Mar 26 '12 at 2:16










          • Sorry, I have fixed my solution now. I needed to use array_unique to remove the duplicate values. You did well with your solution, getting it right is the main thing.
            – Paul
            Mar 26 '12 at 3:59















          up vote
          6
          down vote













          Yes, I would have written this completely differently (so I decided to do just that). I'll follow my solution with a hopefully scientific analysis of both solutions. Finally I'll give some personal recommendations.



          Note: The reason for the in-depth analysis is that I was already interested in pagination and thinking about an implementation for a framework that I am writing.



          Model



          /** Model the data for pagination.
          */
          class ModelPaginator
          {
          /** Get the links for pagination.
          * @param page int The page number for the current page.
          * @param numberOfPages int The total number of paginated pages.
          * @param context int The number of context pages surrounding the current,
          * first and last pages.
          * @return array An array with the ranges of pages for pagination.
          */
          public function getLinks($page, $numberOfPages, $context)
          {
          // Sanitize the inputs (I am trying to follow your example here, I would
          // throw exceptions if the values weren't as I expected).
          $numberOfPages = max($numberOfPages, 1);
          $context = min(max($context, 0), $numberOfPages - 1);
          $page = min(max($page, 1), $numberOfPages);

          return array_unique(
          array_merge(range(1, 1 + $context),
          range(max($page - $context, 1),
          min($page + $context, $numberOfPages)),
          range($numberOfPages - $context, $numberOfPages)));
          }
          }


          View



          /** A simple view for paginator links.
          */
          class ViewPaginatorLinks
          {
          protected $separator;

          /** Construct the paginator.
          * @param separator string Separator between gaps.
          */
          public function __construct($separator=' ... ')
          {
          $this->separator = $separator;
          }

          /** Write the pagination links.
          * @param links array The links array.
          * @param currentPage int The current page.
          */
          public function write($links, $currentPage)
          {
          $currentPage = min(max($currentPage, 1), end($links));

          echo 'Page ' . $currentPage . ' of ' . end($links) . ': ';
          $previousPage = 0;

          foreach ($links as $page) {
          if ($page !== $previousPage + 1) {
          $this->writeSeparator();
          }

          $this->writeLink($page);
          $previousPage = $page;
          }
          }

          /*******************/
          /* Private Methods */
          /*******************/

          /** Write the link to the paginated page.
          * @param page array The page that we are writing the link for.
          */
          private function writeLink($page)
          {
          echo '<a href="?p=' . $page . '" target="_self">' . $page . '</a>' .
          "n";
          }

          /// Write the separator that bridges gaps in the pagination.
          private function writeSeparator()
          {
          echo '<span>' . $this->separator . '</span>' . "n";
          }
          }


          Usage



          $numberOfPages = 29;
          $currentPage = 13;
          $context = 1;

          $Model = new ModelPaginator;
          $View = new ViewPaginatorLinks;

          $View->write($Model->getLinks($currentPage, $numberOfPages, $context),
          $currentPage);


          Analysis



          I'll be going through some metrics thanks to PHPLOC. I added the maximum line length to these metrics.



          Your Solution



          Lines of Code (LOC):                                117
          Cyclomatic Complexity / Lines of Code: 0.37
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 83

          Maximum Line Length 120


          My Solution



          Lines of Code (LOC):                                 97
          Cyclomatic Complexity / Lines of Code: 0.03
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 63

          Maximum Line Length 80


          Lines of Code



          My solution has 20 fewer lines. This could well be due to removing quite a few blank lines. However, measured in characters it is 2905 characters long against your 4089. So there is definitely a large difference in typing required.



          Cyclomatic Complexity



          This is a measure of how complex the code is (see wikipedia). Highly complex code is normally harder to maintain and contains more bugs. My code is a factor of 10 less complex. This is due to the flatness of my code. See Jeff Atwood's Flattening Arrow Code blog.



          Comments



          The number has remained the same according to PHPLOC, but it seems to be counting blank lines. Manually I can only count 14 in your code versus 27 in mine, even though the number of lines of code were reduced in my solution. Proportionally the commenting in my solution is much higher. At the highest level of abstraction (class, function definitions) I have comments where you have virtually none. My comments cover input parameters to functions and can be used in automatic documentation tools like Doxygen or PHPDocumentor.



          Line Length



          Firstly, I am unsure whether the formatting of your code here is exactly as it was. I'll assume the true maximum line length of your code is 120 characters. These days monitors can handle 120 characters wide. However when you are editing two or three files side by side they can't. You also end up with large blank areas for showing the long lines (unless you are ok with it wrapping).



          Performance



          I ran each code 100, 000 times with no output being echoed (the buffer was cleared after each iteration).



          There were a few different configurations that I tested as far as object creation and usage:




          • Creating objects within each loop: Your code was 21% faster.

          • Creating the objects outside of the loop: Your code was 10% faster.

          • Creating the objects outside the loop, getting the model data and running only the view code in the loop: Mine was 27% faster.


          Your code has the better performance, but the 100, 000 iterations only took 5.8 4.2 seconds (see edit below) on my not very fast machine. I think the performance difference is probably due to my split of Model and View. I would only consider changing this split if the code was running on a site with enormous traffic and it was shown by profiling that real gains could be had from changing the pagination.



          Edit: The performance gains from your solution were troubling me. With some profiling I was able to see that the View was wasting a lot of time dispatching the calls with $this->writeSeparator and $this->writeLink. Replacing these calls with their code (seeing as they were only 1 line anyway) led to a large performance gain. My code was faster by 10%, 18% and 59%. Worst case performance is 4.2s for 100 000 iterations (context = 1).



          Real Recommendations



          The following line should not have $p in it. Globals are bad practice and will only make you look bad.



          if (!$page || $p < 1) { $page = 1; }



          Comments belong with the code they are commenting on. A blank line after a comment only distances it from that.



          I think your code was missing a '}' for the end of your function.



          Mixed indentation for if/elseif removes the visual cues for grouping. Instead of:



          if () {
          } else if () {
          }


          Use:



          if () {
          }
          elseif () { // elseif is equivalent to else if
          }


          Similarly I would avoid one line if and if/else statements






          share|improve this answer























          • Cool! But not enough checks! :) What if $context == $numberOfPages in the Model? What if $currentPage > $numberOfPages in the View?
            – Michael
            Mar 23 '12 at 18:06










          • Thanks for that, I've fixed those now and added some analysis.
            – Paul
            Mar 24 '12 at 2:46










          • That is one heck of a reply, thank you!! I think your coding knowledge is above mine ha! I've never used model/classes if I'm honest so that's a little new to me but I'm taking it all in as well as your recommendations! However, I've copied your code (model, view, usage - in that order) and tested it and it doesn't work so I'm assuming I'm probably doing something wrong, but I can't see what exactly. I only changed the $numberOfPages = 9; $currentPage = $_GET['p']; and if I go to page 1 I get the output: Page 1 of 9: 12...12...89 and going to page 2: Page 2 of 9: 12...123...89
            – Joe
            Mar 26 '12 at 2:14










          • Oh and as for missing the closing bracket on the function I think that was me just accidentally deleting it on this text editor, it was there in the real code :)
            – Joe
            Mar 26 '12 at 2:16










          • Sorry, I have fixed my solution now. I needed to use array_unique to remove the duplicate values. You did well with your solution, getting it right is the main thing.
            – Paul
            Mar 26 '12 at 3:59













          up vote
          6
          down vote










          up vote
          6
          down vote









          Yes, I would have written this completely differently (so I decided to do just that). I'll follow my solution with a hopefully scientific analysis of both solutions. Finally I'll give some personal recommendations.



          Note: The reason for the in-depth analysis is that I was already interested in pagination and thinking about an implementation for a framework that I am writing.



          Model



          /** Model the data for pagination.
          */
          class ModelPaginator
          {
          /** Get the links for pagination.
          * @param page int The page number for the current page.
          * @param numberOfPages int The total number of paginated pages.
          * @param context int The number of context pages surrounding the current,
          * first and last pages.
          * @return array An array with the ranges of pages for pagination.
          */
          public function getLinks($page, $numberOfPages, $context)
          {
          // Sanitize the inputs (I am trying to follow your example here, I would
          // throw exceptions if the values weren't as I expected).
          $numberOfPages = max($numberOfPages, 1);
          $context = min(max($context, 0), $numberOfPages - 1);
          $page = min(max($page, 1), $numberOfPages);

          return array_unique(
          array_merge(range(1, 1 + $context),
          range(max($page - $context, 1),
          min($page + $context, $numberOfPages)),
          range($numberOfPages - $context, $numberOfPages)));
          }
          }


          View



          /** A simple view for paginator links.
          */
          class ViewPaginatorLinks
          {
          protected $separator;

          /** Construct the paginator.
          * @param separator string Separator between gaps.
          */
          public function __construct($separator=' ... ')
          {
          $this->separator = $separator;
          }

          /** Write the pagination links.
          * @param links array The links array.
          * @param currentPage int The current page.
          */
          public function write($links, $currentPage)
          {
          $currentPage = min(max($currentPage, 1), end($links));

          echo 'Page ' . $currentPage . ' of ' . end($links) . ': ';
          $previousPage = 0;

          foreach ($links as $page) {
          if ($page !== $previousPage + 1) {
          $this->writeSeparator();
          }

          $this->writeLink($page);
          $previousPage = $page;
          }
          }

          /*******************/
          /* Private Methods */
          /*******************/

          /** Write the link to the paginated page.
          * @param page array The page that we are writing the link for.
          */
          private function writeLink($page)
          {
          echo '<a href="?p=' . $page . '" target="_self">' . $page . '</a>' .
          "n";
          }

          /// Write the separator that bridges gaps in the pagination.
          private function writeSeparator()
          {
          echo '<span>' . $this->separator . '</span>' . "n";
          }
          }


          Usage



          $numberOfPages = 29;
          $currentPage = 13;
          $context = 1;

          $Model = new ModelPaginator;
          $View = new ViewPaginatorLinks;

          $View->write($Model->getLinks($currentPage, $numberOfPages, $context),
          $currentPage);


          Analysis



          I'll be going through some metrics thanks to PHPLOC. I added the maximum line length to these metrics.



          Your Solution



          Lines of Code (LOC):                                117
          Cyclomatic Complexity / Lines of Code: 0.37
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 83

          Maximum Line Length 120


          My Solution



          Lines of Code (LOC):                                 97
          Cyclomatic Complexity / Lines of Code: 0.03
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 63

          Maximum Line Length 80


          Lines of Code



          My solution has 20 fewer lines. This could well be due to removing quite a few blank lines. However, measured in characters it is 2905 characters long against your 4089. So there is definitely a large difference in typing required.



          Cyclomatic Complexity



          This is a measure of how complex the code is (see wikipedia). Highly complex code is normally harder to maintain and contains more bugs. My code is a factor of 10 less complex. This is due to the flatness of my code. See Jeff Atwood's Flattening Arrow Code blog.



          Comments



          The number has remained the same according to PHPLOC, but it seems to be counting blank lines. Manually I can only count 14 in your code versus 27 in mine, even though the number of lines of code were reduced in my solution. Proportionally the commenting in my solution is much higher. At the highest level of abstraction (class, function definitions) I have comments where you have virtually none. My comments cover input parameters to functions and can be used in automatic documentation tools like Doxygen or PHPDocumentor.



          Line Length



          Firstly, I am unsure whether the formatting of your code here is exactly as it was. I'll assume the true maximum line length of your code is 120 characters. These days monitors can handle 120 characters wide. However when you are editing two or three files side by side they can't. You also end up with large blank areas for showing the long lines (unless you are ok with it wrapping).



          Performance



          I ran each code 100, 000 times with no output being echoed (the buffer was cleared after each iteration).



          There were a few different configurations that I tested as far as object creation and usage:




          • Creating objects within each loop: Your code was 21% faster.

          • Creating the objects outside of the loop: Your code was 10% faster.

          • Creating the objects outside the loop, getting the model data and running only the view code in the loop: Mine was 27% faster.


          Your code has the better performance, but the 100, 000 iterations only took 5.8 4.2 seconds (see edit below) on my not very fast machine. I think the performance difference is probably due to my split of Model and View. I would only consider changing this split if the code was running on a site with enormous traffic and it was shown by profiling that real gains could be had from changing the pagination.



          Edit: The performance gains from your solution were troubling me. With some profiling I was able to see that the View was wasting a lot of time dispatching the calls with $this->writeSeparator and $this->writeLink. Replacing these calls with their code (seeing as they were only 1 line anyway) led to a large performance gain. My code was faster by 10%, 18% and 59%. Worst case performance is 4.2s for 100 000 iterations (context = 1).



          Real Recommendations



          The following line should not have $p in it. Globals are bad practice and will only make you look bad.



          if (!$page || $p < 1) { $page = 1; }



          Comments belong with the code they are commenting on. A blank line after a comment only distances it from that.



          I think your code was missing a '}' for the end of your function.



          Mixed indentation for if/elseif removes the visual cues for grouping. Instead of:



          if () {
          } else if () {
          }


          Use:



          if () {
          }
          elseif () { // elseif is equivalent to else if
          }


          Similarly I would avoid one line if and if/else statements






          share|improve this answer














          Yes, I would have written this completely differently (so I decided to do just that). I'll follow my solution with a hopefully scientific analysis of both solutions. Finally I'll give some personal recommendations.



          Note: The reason for the in-depth analysis is that I was already interested in pagination and thinking about an implementation for a framework that I am writing.



          Model



          /** Model the data for pagination.
          */
          class ModelPaginator
          {
          /** Get the links for pagination.
          * @param page int The page number for the current page.
          * @param numberOfPages int The total number of paginated pages.
          * @param context int The number of context pages surrounding the current,
          * first and last pages.
          * @return array An array with the ranges of pages for pagination.
          */
          public function getLinks($page, $numberOfPages, $context)
          {
          // Sanitize the inputs (I am trying to follow your example here, I would
          // throw exceptions if the values weren't as I expected).
          $numberOfPages = max($numberOfPages, 1);
          $context = min(max($context, 0), $numberOfPages - 1);
          $page = min(max($page, 1), $numberOfPages);

          return array_unique(
          array_merge(range(1, 1 + $context),
          range(max($page - $context, 1),
          min($page + $context, $numberOfPages)),
          range($numberOfPages - $context, $numberOfPages)));
          }
          }


          View



          /** A simple view for paginator links.
          */
          class ViewPaginatorLinks
          {
          protected $separator;

          /** Construct the paginator.
          * @param separator string Separator between gaps.
          */
          public function __construct($separator=' ... ')
          {
          $this->separator = $separator;
          }

          /** Write the pagination links.
          * @param links array The links array.
          * @param currentPage int The current page.
          */
          public function write($links, $currentPage)
          {
          $currentPage = min(max($currentPage, 1), end($links));

          echo 'Page ' . $currentPage . ' of ' . end($links) . ': ';
          $previousPage = 0;

          foreach ($links as $page) {
          if ($page !== $previousPage + 1) {
          $this->writeSeparator();
          }

          $this->writeLink($page);
          $previousPage = $page;
          }
          }

          /*******************/
          /* Private Methods */
          /*******************/

          /** Write the link to the paginated page.
          * @param page array The page that we are writing the link for.
          */
          private function writeLink($page)
          {
          echo '<a href="?p=' . $page . '" target="_self">' . $page . '</a>' .
          "n";
          }

          /// Write the separator that bridges gaps in the pagination.
          private function writeSeparator()
          {
          echo '<span>' . $this->separator . '</span>' . "n";
          }
          }


          Usage



          $numberOfPages = 29;
          $currentPage = 13;
          $context = 1;

          $Model = new ModelPaginator;
          $View = new ViewPaginatorLinks;

          $View->write($Model->getLinks($currentPage, $numberOfPages, $context),
          $currentPage);


          Analysis



          I'll be going through some metrics thanks to PHPLOC. I added the maximum line length to these metrics.



          Your Solution



          Lines of Code (LOC):                                117
          Cyclomatic Complexity / Lines of Code: 0.37
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 83

          Maximum Line Length 120


          My Solution



          Lines of Code (LOC):                                 97
          Cyclomatic Complexity / Lines of Code: 0.03
          Comment Lines of Code (CLOC): 34
          Non-Comment Lines of Code (NCLOC): 63

          Maximum Line Length 80


          Lines of Code



          My solution has 20 fewer lines. This could well be due to removing quite a few blank lines. However, measured in characters it is 2905 characters long against your 4089. So there is definitely a large difference in typing required.



          Cyclomatic Complexity



          This is a measure of how complex the code is (see wikipedia). Highly complex code is normally harder to maintain and contains more bugs. My code is a factor of 10 less complex. This is due to the flatness of my code. See Jeff Atwood's Flattening Arrow Code blog.



          Comments



          The number has remained the same according to PHPLOC, but it seems to be counting blank lines. Manually I can only count 14 in your code versus 27 in mine, even though the number of lines of code were reduced in my solution. Proportionally the commenting in my solution is much higher. At the highest level of abstraction (class, function definitions) I have comments where you have virtually none. My comments cover input parameters to functions and can be used in automatic documentation tools like Doxygen or PHPDocumentor.



          Line Length



          Firstly, I am unsure whether the formatting of your code here is exactly as it was. I'll assume the true maximum line length of your code is 120 characters. These days monitors can handle 120 characters wide. However when you are editing two or three files side by side they can't. You also end up with large blank areas for showing the long lines (unless you are ok with it wrapping).



          Performance



          I ran each code 100, 000 times with no output being echoed (the buffer was cleared after each iteration).



          There were a few different configurations that I tested as far as object creation and usage:




          • Creating objects within each loop: Your code was 21% faster.

          • Creating the objects outside of the loop: Your code was 10% faster.

          • Creating the objects outside the loop, getting the model data and running only the view code in the loop: Mine was 27% faster.


          Your code has the better performance, but the 100, 000 iterations only took 5.8 4.2 seconds (see edit below) on my not very fast machine. I think the performance difference is probably due to my split of Model and View. I would only consider changing this split if the code was running on a site with enormous traffic and it was shown by profiling that real gains could be had from changing the pagination.



          Edit: The performance gains from your solution were troubling me. With some profiling I was able to see that the View was wasting a lot of time dispatching the calls with $this->writeSeparator and $this->writeLink. Replacing these calls with their code (seeing as they were only 1 line anyway) led to a large performance gain. My code was faster by 10%, 18% and 59%. Worst case performance is 4.2s for 100 000 iterations (context = 1).



          Real Recommendations



          The following line should not have $p in it. Globals are bad practice and will only make you look bad.



          if (!$page || $p < 1) { $page = 1; }



          Comments belong with the code they are commenting on. A blank line after a comment only distances it from that.



          I think your code was missing a '}' for the end of your function.



          Mixed indentation for if/elseif removes the visual cues for grouping. Instead of:



          if () {
          } else if () {
          }


          Use:



          if () {
          }
          elseif () { // elseif is equivalent to else if
          }


          Similarly I would avoid one line if and if/else statements







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 13 hours ago









          albert

          1371




          1371










          answered Mar 23 '12 at 17:23









          Paul

          3,72121334




          3,72121334












          • Cool! But not enough checks! :) What if $context == $numberOfPages in the Model? What if $currentPage > $numberOfPages in the View?
            – Michael
            Mar 23 '12 at 18:06










          • Thanks for that, I've fixed those now and added some analysis.
            – Paul
            Mar 24 '12 at 2:46










          • That is one heck of a reply, thank you!! I think your coding knowledge is above mine ha! I've never used model/classes if I'm honest so that's a little new to me but I'm taking it all in as well as your recommendations! However, I've copied your code (model, view, usage - in that order) and tested it and it doesn't work so I'm assuming I'm probably doing something wrong, but I can't see what exactly. I only changed the $numberOfPages = 9; $currentPage = $_GET['p']; and if I go to page 1 I get the output: Page 1 of 9: 12...12...89 and going to page 2: Page 2 of 9: 12...123...89
            – Joe
            Mar 26 '12 at 2:14










          • Oh and as for missing the closing bracket on the function I think that was me just accidentally deleting it on this text editor, it was there in the real code :)
            – Joe
            Mar 26 '12 at 2:16










          • Sorry, I have fixed my solution now. I needed to use array_unique to remove the duplicate values. You did well with your solution, getting it right is the main thing.
            – Paul
            Mar 26 '12 at 3:59


















          • Cool! But not enough checks! :) What if $context == $numberOfPages in the Model? What if $currentPage > $numberOfPages in the View?
            – Michael
            Mar 23 '12 at 18:06










          • Thanks for that, I've fixed those now and added some analysis.
            – Paul
            Mar 24 '12 at 2:46










          • That is one heck of a reply, thank you!! I think your coding knowledge is above mine ha! I've never used model/classes if I'm honest so that's a little new to me but I'm taking it all in as well as your recommendations! However, I've copied your code (model, view, usage - in that order) and tested it and it doesn't work so I'm assuming I'm probably doing something wrong, but I can't see what exactly. I only changed the $numberOfPages = 9; $currentPage = $_GET['p']; and if I go to page 1 I get the output: Page 1 of 9: 12...12...89 and going to page 2: Page 2 of 9: 12...123...89
            – Joe
            Mar 26 '12 at 2:14










          • Oh and as for missing the closing bracket on the function I think that was me just accidentally deleting it on this text editor, it was there in the real code :)
            – Joe
            Mar 26 '12 at 2:16










          • Sorry, I have fixed my solution now. I needed to use array_unique to remove the duplicate values. You did well with your solution, getting it right is the main thing.
            – Paul
            Mar 26 '12 at 3:59
















          Cool! But not enough checks! :) What if $context == $numberOfPages in the Model? What if $currentPage > $numberOfPages in the View?
          – Michael
          Mar 23 '12 at 18:06




          Cool! But not enough checks! :) What if $context == $numberOfPages in the Model? What if $currentPage > $numberOfPages in the View?
          – Michael
          Mar 23 '12 at 18:06












          Thanks for that, I've fixed those now and added some analysis.
          – Paul
          Mar 24 '12 at 2:46




          Thanks for that, I've fixed those now and added some analysis.
          – Paul
          Mar 24 '12 at 2:46












          That is one heck of a reply, thank you!! I think your coding knowledge is above mine ha! I've never used model/classes if I'm honest so that's a little new to me but I'm taking it all in as well as your recommendations! However, I've copied your code (model, view, usage - in that order) and tested it and it doesn't work so I'm assuming I'm probably doing something wrong, but I can't see what exactly. I only changed the $numberOfPages = 9; $currentPage = $_GET['p']; and if I go to page 1 I get the output: Page 1 of 9: 12...12...89 and going to page 2: Page 2 of 9: 12...123...89
          – Joe
          Mar 26 '12 at 2:14




          That is one heck of a reply, thank you!! I think your coding knowledge is above mine ha! I've never used model/classes if I'm honest so that's a little new to me but I'm taking it all in as well as your recommendations! However, I've copied your code (model, view, usage - in that order) and tested it and it doesn't work so I'm assuming I'm probably doing something wrong, but I can't see what exactly. I only changed the $numberOfPages = 9; $currentPage = $_GET['p']; and if I go to page 1 I get the output: Page 1 of 9: 12...12...89 and going to page 2: Page 2 of 9: 12...123...89
          – Joe
          Mar 26 '12 at 2:14












          Oh and as for missing the closing bracket on the function I think that was me just accidentally deleting it on this text editor, it was there in the real code :)
          – Joe
          Mar 26 '12 at 2:16




          Oh and as for missing the closing bracket on the function I think that was me just accidentally deleting it on this text editor, it was there in the real code :)
          – Joe
          Mar 26 '12 at 2:16












          Sorry, I have fixed my solution now. I needed to use array_unique to remove the duplicate values. You did well with your solution, getting it right is the main thing.
          – Paul
          Mar 26 '12 at 3:59




          Sorry, I have fixed my solution now. I needed to use array_unique to remove the duplicate values. You did well with your solution, getting it right is the main thing.
          – Paul
          Mar 26 '12 at 3:59












          up vote
          0
          down vote













          You can cut out some redundancy by just using one array to store the results, and you can cut down on the number of operations by doing simple checks against what we know will be the last and first members of the outer page groups instead of checking whether those numbers are in the right/left arrays. If you wanted to be super fancy, and save even more operations, you could store all those values in variables so you're not doing the arithmetic operations every iteration in the loops, but I didn't bother.



          I'm not too well versed in php, but here's a version with some changes. I was able to cut about 40 lines.



          <?php

          // Global Variables

          $p = $_GET['p']; // Current Page
          $t = 9; // Total Pages
          $c = 1; // Context Pages

          // Pagination Function
          function outputLinks($page, $numberOfPages, $context)
          {
          $display = array();

          //Left-side pages
          for($i = 1; $i <= 1 + $context; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          if(($page - $context) - (1 + $context) > 1)
          array_push($display, "...");

          //Middle pages
          for($i = ($page - $context); $i <= ($page + $context); $i++)
          {
          if($i > (1 + $context) && $i < ($numberOfPages - $context))
          array_push($display, buildPageLink($i));
          }

          if($page + $context < $numberOfPages - $context)
          array_push($display, "...");

          //Right-side pages
          for($i = $numberOfPages - $context; $i <= $numberOfPages; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          echo 'Page ' . $page . ' of ' .$numberOfPages . ': ';
          foreach($display as $val)
          {
          echo $val;
          }

          }

          function buildPageLink($pagenum)
          {
          return ' <a href="?p='.$pagenum.'" target="_self">'.$pagenum.'</a> ';
          }

          // Initialize document

          print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

          // Generate Pagination through the outputLinks function

          outputLinks($p, $t, $c);

          // Close the document

          print('</body></html>');

          ?>





          share|improve this answer





















          • That is definitely a better approach than mine but it doesn't work in all scenarios like mine does. If you changed numberOfPages from 9 to 3, you get Page 1 of 3: 1 2 2 3 and also if you have more context pages than numberOfPages you get problems, for example $numberOfPages = 9 and $context = 10 you get Page 9 of 9: 1 2 3 4 5 6 7 8 9 10 11 -1 0 1 2 3 4 5 6 7 8 9 - I know this can be fixed in your code too. But just wanted to point out it doesn't work in every case. Thank you for the ideas though!
            – Joe
            Mar 26 '12 at 1:57















          up vote
          0
          down vote













          You can cut out some redundancy by just using one array to store the results, and you can cut down on the number of operations by doing simple checks against what we know will be the last and first members of the outer page groups instead of checking whether those numbers are in the right/left arrays. If you wanted to be super fancy, and save even more operations, you could store all those values in variables so you're not doing the arithmetic operations every iteration in the loops, but I didn't bother.



          I'm not too well versed in php, but here's a version with some changes. I was able to cut about 40 lines.



          <?php

          // Global Variables

          $p = $_GET['p']; // Current Page
          $t = 9; // Total Pages
          $c = 1; // Context Pages

          // Pagination Function
          function outputLinks($page, $numberOfPages, $context)
          {
          $display = array();

          //Left-side pages
          for($i = 1; $i <= 1 + $context; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          if(($page - $context) - (1 + $context) > 1)
          array_push($display, "...");

          //Middle pages
          for($i = ($page - $context); $i <= ($page + $context); $i++)
          {
          if($i > (1 + $context) && $i < ($numberOfPages - $context))
          array_push($display, buildPageLink($i));
          }

          if($page + $context < $numberOfPages - $context)
          array_push($display, "...");

          //Right-side pages
          for($i = $numberOfPages - $context; $i <= $numberOfPages; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          echo 'Page ' . $page . ' of ' .$numberOfPages . ': ';
          foreach($display as $val)
          {
          echo $val;
          }

          }

          function buildPageLink($pagenum)
          {
          return ' <a href="?p='.$pagenum.'" target="_self">'.$pagenum.'</a> ';
          }

          // Initialize document

          print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

          // Generate Pagination through the outputLinks function

          outputLinks($p, $t, $c);

          // Close the document

          print('</body></html>');

          ?>





          share|improve this answer





















          • That is definitely a better approach than mine but it doesn't work in all scenarios like mine does. If you changed numberOfPages from 9 to 3, you get Page 1 of 3: 1 2 2 3 and also if you have more context pages than numberOfPages you get problems, for example $numberOfPages = 9 and $context = 10 you get Page 9 of 9: 1 2 3 4 5 6 7 8 9 10 11 -1 0 1 2 3 4 5 6 7 8 9 - I know this can be fixed in your code too. But just wanted to point out it doesn't work in every case. Thank you for the ideas though!
            – Joe
            Mar 26 '12 at 1:57













          up vote
          0
          down vote










          up vote
          0
          down vote









          You can cut out some redundancy by just using one array to store the results, and you can cut down on the number of operations by doing simple checks against what we know will be the last and first members of the outer page groups instead of checking whether those numbers are in the right/left arrays. If you wanted to be super fancy, and save even more operations, you could store all those values in variables so you're not doing the arithmetic operations every iteration in the loops, but I didn't bother.



          I'm not too well versed in php, but here's a version with some changes. I was able to cut about 40 lines.



          <?php

          // Global Variables

          $p = $_GET['p']; // Current Page
          $t = 9; // Total Pages
          $c = 1; // Context Pages

          // Pagination Function
          function outputLinks($page, $numberOfPages, $context)
          {
          $display = array();

          //Left-side pages
          for($i = 1; $i <= 1 + $context; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          if(($page - $context) - (1 + $context) > 1)
          array_push($display, "...");

          //Middle pages
          for($i = ($page - $context); $i <= ($page + $context); $i++)
          {
          if($i > (1 + $context) && $i < ($numberOfPages - $context))
          array_push($display, buildPageLink($i));
          }

          if($page + $context < $numberOfPages - $context)
          array_push($display, "...");

          //Right-side pages
          for($i = $numberOfPages - $context; $i <= $numberOfPages; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          echo 'Page ' . $page . ' of ' .$numberOfPages . ': ';
          foreach($display as $val)
          {
          echo $val;
          }

          }

          function buildPageLink($pagenum)
          {
          return ' <a href="?p='.$pagenum.'" target="_self">'.$pagenum.'</a> ';
          }

          // Initialize document

          print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

          // Generate Pagination through the outputLinks function

          outputLinks($p, $t, $c);

          // Close the document

          print('</body></html>');

          ?>





          share|improve this answer












          You can cut out some redundancy by just using one array to store the results, and you can cut down on the number of operations by doing simple checks against what we know will be the last and first members of the outer page groups instead of checking whether those numbers are in the right/left arrays. If you wanted to be super fancy, and save even more operations, you could store all those values in variables so you're not doing the arithmetic operations every iteration in the loops, but I didn't bother.



          I'm not too well versed in php, but here's a version with some changes. I was able to cut about 40 lines.



          <?php

          // Global Variables

          $p = $_GET['p']; // Current Page
          $t = 9; // Total Pages
          $c = 1; // Context Pages

          // Pagination Function
          function outputLinks($page, $numberOfPages, $context)
          {
          $display = array();

          //Left-side pages
          for($i = 1; $i <= 1 + $context; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          if(($page - $context) - (1 + $context) > 1)
          array_push($display, "...");

          //Middle pages
          for($i = ($page - $context); $i <= ($page + $context); $i++)
          {
          if($i > (1 + $context) && $i < ($numberOfPages - $context))
          array_push($display, buildPageLink($i));
          }

          if($page + $context < $numberOfPages - $context)
          array_push($display, "...");

          //Right-side pages
          for($i = $numberOfPages - $context; $i <= $numberOfPages; $i++)
          {
          array_push($display, buildPageLink($i));
          }

          echo 'Page ' . $page . ' of ' .$numberOfPages . ': ';
          foreach($display as $val)
          {
          echo $val;
          }

          }

          function buildPageLink($pagenum)
          {
          return ' <a href="?p='.$pagenum.'" target="_self">'.$pagenum.'</a> ';
          }

          // Initialize document

          print('<!DOCTYPE HTML><html><head><title>Pagination Function</title></head><body>');

          // Generate Pagination through the outputLinks function

          outputLinks($p, $t, $c);

          // Close the document

          print('</body></html>');

          ?>






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Mar 23 '12 at 15:46









          Eric C.

          1011




          1011












          • That is definitely a better approach than mine but it doesn't work in all scenarios like mine does. If you changed numberOfPages from 9 to 3, you get Page 1 of 3: 1 2 2 3 and also if you have more context pages than numberOfPages you get problems, for example $numberOfPages = 9 and $context = 10 you get Page 9 of 9: 1 2 3 4 5 6 7 8 9 10 11 -1 0 1 2 3 4 5 6 7 8 9 - I know this can be fixed in your code too. But just wanted to point out it doesn't work in every case. Thank you for the ideas though!
            – Joe
            Mar 26 '12 at 1:57


















          • That is definitely a better approach than mine but it doesn't work in all scenarios like mine does. If you changed numberOfPages from 9 to 3, you get Page 1 of 3: 1 2 2 3 and also if you have more context pages than numberOfPages you get problems, for example $numberOfPages = 9 and $context = 10 you get Page 9 of 9: 1 2 3 4 5 6 7 8 9 10 11 -1 0 1 2 3 4 5 6 7 8 9 - I know this can be fixed in your code too. But just wanted to point out it doesn't work in every case. Thank you for the ideas though!
            – Joe
            Mar 26 '12 at 1:57
















          That is definitely a better approach than mine but it doesn't work in all scenarios like mine does. If you changed numberOfPages from 9 to 3, you get Page 1 of 3: 1 2 2 3 and also if you have more context pages than numberOfPages you get problems, for example $numberOfPages = 9 and $context = 10 you get Page 9 of 9: 1 2 3 4 5 6 7 8 9 10 11 -1 0 1 2 3 4 5 6 7 8 9 - I know this can be fixed in your code too. But just wanted to point out it doesn't work in every case. Thank you for the ideas though!
          – Joe
          Mar 26 '12 at 1:57




          That is definitely a better approach than mine but it doesn't work in all scenarios like mine does. If you changed numberOfPages from 9 to 3, you get Page 1 of 3: 1 2 2 3 and also if you have more context pages than numberOfPages you get problems, for example $numberOfPages = 9 and $context = 10 you get Page 9 of 9: 1 2 3 4 5 6 7 8 9 10 11 -1 0 1 2 3 4 5 6 7 8 9 - I know this can be fixed in your code too. But just wanted to point out it doesn't work in every case. Thank you for the ideas though!
          – Joe
          Mar 26 '12 at 1:57










          up vote
          0
          down vote













          <?php
          function outputLinks( $page, $numberOfPages, $context ) {
          # find the context pages
          $pages = array_filter( range( $page - $context, $page + $context ), function( $value ) use ($numberOfPages) {
          if ( $value <= 2 || $value >= ( $numberOfPages - 1 ) ) return false;
          return true;
          });

          # are there any gaps to fill with '...':
          if ( isset( $pages[0] ) ) {
          if ( $pages[0] > 2 ) array_unshift( $pages, '...' );
          if ( $pages[ count( $pages ) - 1 ] < ( $numberOfPages - 2 ) ) array_push( $pages, '...' );
          }

          # add the first and last links:
          array_push( $pages, $numberOfPages - 1, $numberOfPages );
          array_unshift( $pages, 1, 2 );

          foreach ( $pages as $key => & $page ) {
          # if ( is_int( $page ) ) $page = '<a href="?p=' . $page . '" />';
          }

          return $pages;
          }
          # your example with my impl:
          for ( $i = 1; $i <= 9; $i++ ) echo implode(' ', outputLinks( $i, 9, 1 ), PHP_EOL;


          I had to give this one a shot aiming for the least amount of lines logic as possible, so here's my solution. It's split in 4 "blocks":
          - find the context pages
          - fill in blanks between the beginning/ending and the context pages with '...'
          - add the first/last pages
          - lastly, loop through the results ( uncomment line 20 ) and make them into links.



          I can't think of anything simpler, it's already down to 16 lines of code.



          Cheers






          share|improve this answer





















          • There are different but equally valid assumptions with the first and last pages which have a fixed context of 1 page in this solution. There is a small bug, it prints 1289 without '...' between the 2 and 8. Also a copy paste error on the final line with the ) and ,.
            – Paul
            Apr 6 '12 at 2:23















          up vote
          0
          down vote













          <?php
          function outputLinks( $page, $numberOfPages, $context ) {
          # find the context pages
          $pages = array_filter( range( $page - $context, $page + $context ), function( $value ) use ($numberOfPages) {
          if ( $value <= 2 || $value >= ( $numberOfPages - 1 ) ) return false;
          return true;
          });

          # are there any gaps to fill with '...':
          if ( isset( $pages[0] ) ) {
          if ( $pages[0] > 2 ) array_unshift( $pages, '...' );
          if ( $pages[ count( $pages ) - 1 ] < ( $numberOfPages - 2 ) ) array_push( $pages, '...' );
          }

          # add the first and last links:
          array_push( $pages, $numberOfPages - 1, $numberOfPages );
          array_unshift( $pages, 1, 2 );

          foreach ( $pages as $key => & $page ) {
          # if ( is_int( $page ) ) $page = '<a href="?p=' . $page . '" />';
          }

          return $pages;
          }
          # your example with my impl:
          for ( $i = 1; $i <= 9; $i++ ) echo implode(' ', outputLinks( $i, 9, 1 ), PHP_EOL;


          I had to give this one a shot aiming for the least amount of lines logic as possible, so here's my solution. It's split in 4 "blocks":
          - find the context pages
          - fill in blanks between the beginning/ending and the context pages with '...'
          - add the first/last pages
          - lastly, loop through the results ( uncomment line 20 ) and make them into links.



          I can't think of anything simpler, it's already down to 16 lines of code.



          Cheers






          share|improve this answer





















          • There are different but equally valid assumptions with the first and last pages which have a fixed context of 1 page in this solution. There is a small bug, it prints 1289 without '...' between the 2 and 8. Also a copy paste error on the final line with the ) and ,.
            – Paul
            Apr 6 '12 at 2:23













          up vote
          0
          down vote










          up vote
          0
          down vote









          <?php
          function outputLinks( $page, $numberOfPages, $context ) {
          # find the context pages
          $pages = array_filter( range( $page - $context, $page + $context ), function( $value ) use ($numberOfPages) {
          if ( $value <= 2 || $value >= ( $numberOfPages - 1 ) ) return false;
          return true;
          });

          # are there any gaps to fill with '...':
          if ( isset( $pages[0] ) ) {
          if ( $pages[0] > 2 ) array_unshift( $pages, '...' );
          if ( $pages[ count( $pages ) - 1 ] < ( $numberOfPages - 2 ) ) array_push( $pages, '...' );
          }

          # add the first and last links:
          array_push( $pages, $numberOfPages - 1, $numberOfPages );
          array_unshift( $pages, 1, 2 );

          foreach ( $pages as $key => & $page ) {
          # if ( is_int( $page ) ) $page = '<a href="?p=' . $page . '" />';
          }

          return $pages;
          }
          # your example with my impl:
          for ( $i = 1; $i <= 9; $i++ ) echo implode(' ', outputLinks( $i, 9, 1 ), PHP_EOL;


          I had to give this one a shot aiming for the least amount of lines logic as possible, so here's my solution. It's split in 4 "blocks":
          - find the context pages
          - fill in blanks between the beginning/ending and the context pages with '...'
          - add the first/last pages
          - lastly, loop through the results ( uncomment line 20 ) and make them into links.



          I can't think of anything simpler, it's already down to 16 lines of code.



          Cheers






          share|improve this answer












          <?php
          function outputLinks( $page, $numberOfPages, $context ) {
          # find the context pages
          $pages = array_filter( range( $page - $context, $page + $context ), function( $value ) use ($numberOfPages) {
          if ( $value <= 2 || $value >= ( $numberOfPages - 1 ) ) return false;
          return true;
          });

          # are there any gaps to fill with '...':
          if ( isset( $pages[0] ) ) {
          if ( $pages[0] > 2 ) array_unshift( $pages, '...' );
          if ( $pages[ count( $pages ) - 1 ] < ( $numberOfPages - 2 ) ) array_push( $pages, '...' );
          }

          # add the first and last links:
          array_push( $pages, $numberOfPages - 1, $numberOfPages );
          array_unshift( $pages, 1, 2 );

          foreach ( $pages as $key => & $page ) {
          # if ( is_int( $page ) ) $page = '<a href="?p=' . $page . '" />';
          }

          return $pages;
          }
          # your example with my impl:
          for ( $i = 1; $i <= 9; $i++ ) echo implode(' ', outputLinks( $i, 9, 1 ), PHP_EOL;


          I had to give this one a shot aiming for the least amount of lines logic as possible, so here's my solution. It's split in 4 "blocks":
          - find the context pages
          - fill in blanks between the beginning/ending and the context pages with '...'
          - add the first/last pages
          - lastly, loop through the results ( uncomment line 20 ) and make them into links.



          I can't think of anything simpler, it's already down to 16 lines of code.



          Cheers







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Apr 5 '12 at 10:31









          smassey

          1514




          1514












          • There are different but equally valid assumptions with the first and last pages which have a fixed context of 1 page in this solution. There is a small bug, it prints 1289 without '...' between the 2 and 8. Also a copy paste error on the final line with the ) and ,.
            – Paul
            Apr 6 '12 at 2:23


















          • There are different but equally valid assumptions with the first and last pages which have a fixed context of 1 page in this solution. There is a small bug, it prints 1289 without '...' between the 2 and 8. Also a copy paste error on the final line with the ) and ,.
            – Paul
            Apr 6 '12 at 2:23
















          There are different but equally valid assumptions with the first and last pages which have a fixed context of 1 page in this solution. There is a small bug, it prints 1289 without '...' between the 2 and 8. Also a copy paste error on the final line with the ) and ,.
          – Paul
          Apr 6 '12 at 2:23




          There are different but equally valid assumptions with the first and last pages which have a fixed context of 1 page in this solution. There is a small bug, it prints 1289 without '...' between the 2 and 8. Also a copy paste error on the final line with the ) and ,.
          – Paul
          Apr 6 '12 at 2:23


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f10271%2fgenerating-pagination-links%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Quarter-circle Tiles

          build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

          Mont Emei