Skip to content

Instantly share code, notes, and snippets.

@kottenator
Created July 13, 2015 20:44
Show Gist options
  • Save kottenator/9d936eb3e4e3c3e02598 to your computer and use it in GitHub Desktop.
Save kottenator/9d936eb3e4e3c3e02598 to your computer and use it in GitHub Desktop.
Simple pagination algorithm
// Implementation in ES6
function pagination(c, m) {
var current = c,
last = m,
delta = 2,
left = current - delta,
right = current + delta + 1,
range = [],
rangeWithDots = [],
l;
for (let i = 1; i <= last; i++) {
if (i == 1 || i == last || i >= left && i < right) {
range.push(i);
}
}
for (let i of range) {
if (l) {
if (i - l === 2) {
rangeWithDots.push(l + 1);
} else if (i - l !== 1) {
rangeWithDots.push('...');
}
}
rangeWithDots.push(i);
l = i;
}
return rangeWithDots;
}
/*
Test it:
for (let i = 1, l = 20; i <= l; i++)
console.log(`Selected page ${i}:`, pagination(i, l));
Expected output:
Selected page 1: [1, 2, 3, "...", 20]
Selected page 2: [1, 2, 3, 4, "...", 20]
Selected page 3: [1, 2, 3, 4, 5, "...", 20]
Selected page 4: [1, 2, 3, 4, 5, 6, "...", 20]
Selected page 5: [1, 2, 3, 4, 5, 6, 7, "...", 20]
Selected page 6: [1, "...", 4, 5, 6, 7, 8, "...", 20]
Selected page 7: [1, "...", 5, 6, 7, 8, 9, "...", 20]
Selected page 8: [1, "...", 6, 7, 8, 9, 10, "...", 20]
Selected page 9: [1, "...", 7, 8, 9, 10, 11, "...", 20]
Selected page 10: [1, "...", 8, 9, 10, 11, 12, "...", 20]
Selected page 11: [1, "...", 9, 10, 11, 12, 13, "...", 20]
Selected page 12: [1, "...", 10, 11, 12, 13, 14, "...", 20]
Selected page 13: [1, "...", 11, 12, 13, 14, 15, "...", 20]
Selected page 14: [1, "...", 12, 13, 14, 15, 16, "...", 20]
Selected page 15: [1, "...", 13, 14, 15, 16, 17, "...", 20]
Selected page 16: [1, "...", 14, 15, 16, 17, 18, 19, 20]
Selected page 17: [1, "...", 15, 16, 17, 18, 19, 20]
Selected page 18: [1, "...", 16, 17, 18, 19, 20]
Selected page 19: [1, "...", 17, 18, 19, 20]
Selected page 20: [1, "...", 18, 19, 20]
*/
@justingolden21
Copy link

Updated for ES6, deleted unused variables, customizable delta, first and last arrows, doesn't break if 1 page, and highlight the current page:

function pagination(current, last, delta = 2) {
  if (last === 1) return [1];

  const left = current - delta,
    right = current + delta + 1,
    range = [];

  if (last > 1 && current !== 1) {
    range.push("<");
  }

  for (let i = 1; i <= last; i++) {
    if (i == 1 || i == last || (i >= left && i < right)) {
      if (i === left && i > 2) {
        range.push("...");
      }

      if (i === current) {
        range.push("*" + i + "*");
      } else {
        range.push(i);
      }

      if (i === right - 1 && i < last - 1) {
        range.push("...");
      }
    }
  }

  if (last > 1 && current !== last) {
    range.push(">");
  }

  return range;
}

for (let i = 1, l = 20; i <= l; i++)
  console.log(`Selected page ${i}:`, pagination(i, l));

@shubhasheeshShukla
Copy link

Thanks a lot sir

@gynekolog
Copy link

Thank you for sharing. I made a few changes and checks:

// The code is based on https://gist.github.com/kottenator/9d936eb3e4e3c3e02598#gistcomment-3238804

type PageItem = number | "...";

export const getRange = (start: number, end: number): PageItem[] => {
  if (end < start) throw Error(`End number must be higher then start number: start ${start}, end ${start}`);

  const rangeLength = end - start + 1;
  return Array(rangeLength)
    .fill(0)
    .map((_, i) => i + start);
};

const clamp = (value: number, lower: number, upper: number) => Math.min(Math.max(value, lower), upper);

export const calculatePages = (currentPage: number, pageCount: number, size: number): PageItem[] => {
  if (pageCount < 1) {
    console.warn("Page count has negative value. Returning empty array.");
    return [];
  }

  if (currentPage < 1) {
    console.warn("Current page has negative value. Current page will be set to 1");
    currentPage = 1;
  }

  if (currentPage > pageCount) {
    console.warn("Current page is higher than page count. Current page will be set to page count:", pageCount);
    currentPage = pageCount;
  }

  if (size % 2 === 0) {
    console.warn("The size must be odd. The size will be increased by 1");
    size += 1;
  }

  if (size < 5) {
    console.warn("The minimum size is 5. The size will be increased to 5");
    size = 5;
  }

  const offset = (size - 1) / 2;
  const shouldAddDots = pageCount > size;

  const rangeConfig = {
    start: clamp(currentPage - offset, 1, shouldAddDots ? pageCount - size + 1 : 1),
    end: clamp(currentPage + offset, size, pageCount),
  };

  const pages = getRange(rangeConfig.start, rangeConfig.end);

  if (shouldAddDots && pages[0] !== 1) {
    pages[0] = 1;
    pages[1] = "...";
  }
  if (shouldAddDots && pages[pages.length - 1] !== pageCount) {
    pages[pages.length - 1] = pageCount;
    pages[pages.length - 2] = "...";
  }
  return pages;
};

example of the output for size 5:

[
  [1, [1, 2, 3, "...", 20]],
  [2, [1, 2, 3, "...", 20]],
  [3, [1, 2, 3, "...", 20]],
  [4, [1, "...", 4, "...", 20]],
  [5, [1, "...", 5, "...", 20]],
  [6, [1, "...", 6, "...", 20]],
  [7, [1, "...", 7, "...", 20]],
  [8, [1, "...", 8, "...", 20]],
  [9, [1, "...", 9, "...", 20]],
  [10, [1, "...", 10, "...", 20]],
  [11, [1, "...", 11, "...", 20]],
  [12, [1, "...", 12, "...", 20]],
  [13, [1, "...", 13, "...", 20]],
  [14, [1, "...", 14, "...", 20]],
  [15, [1, "...", 15, "...", 20]],
  [16, [1, "...", 16, "...", 20]],
  [17, [1, "...", 17, "...", 20]],
  [18, [1, "...", 18, 19, 20]],
  [19, [1, "...", 18, 19, 20]],
  [20, [1, "...", 18, 19, 20]],
];

The code with test is available in my gist too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment