128

I am trying to write a Javascript function that takes an array, page_size and page_number as parameters and returns an array that mimics paginated results:

paginate: function (array, page_size, page_number) {
  return result;
}

so for example when:

array = [1, 2, 3, 4, 5],
page size = 2,
page_number = 2,

the function should return: [3, 4].

Any ideas would be appreciated.

SalmaFG
  • 2,022
  • 3
  • 23
  • 36
  • 1
    What does `[3, 4]` represent in relation to all the other variables? It's kind of key that you explain that explicitly – StudioTime Mar 13 '17 at 10:14
  • 2
    Use [`Array.slice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) -> `array.slice((page_number - 1) * page_size, page_size)` (something like that should work) – casraf Mar 13 '17 at 10:15
  • @DarrenSweeney If the array is divided into "pages", we will get 3 pages where each page is of size 2 at most: `[1, 2]`, `[3, 4]` and `[5]`. Page number 2 in this scenario will be `[3, 4]`. – SalmaFG Mar 13 '17 at 10:18
  • 1
    https://shouts.dev/easiest-way-to-paginate-an-array-in-javascript – SagitSri Jul 13 '21 at 15:24

14 Answers14

333

You can use Array.prototype.slice and just supply the params for (start, end).

function paginate(array, page_size, page_number) {
  // human-readable page numbers usually start with 1, so we reduce 1 in the first argument
  return array.slice((page_number - 1) * page_size, page_number * page_size);
}

console.log(paginate([1, 2, 3, 4, 5, 6], 2, 2));
console.log(paginate([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 4, 1));
casraf
  • 21,085
  • 9
  • 56
  • 91
  • @casraf: You are decreasing `page_number` first and then adding 1 to while slicing at `page_number + 1`. I didn't get whats the logic behind that. I may seem naive but can you please explain that? – Parth Mistry Jul 30 '18 at 07:58
  • 2
    It can be done in any order you want, but slice takes a 0-index number (slice from 0th item up to nth), and page sizes in links & other human readable formats usually start pages at 1 and not 0. Since we start with 1 in the front end but in actuality we use 0, I decrease it and increase it at the relevant time. You could just as easily only decrease page_number in the first argument instead of the second – casraf Aug 12 '18 at 14:15
  • The solution does work fine but how can I understand that I am on the last page? – Avishek Jul 24 '23 at 07:12
  • Just divide the length of the array (amount of items) by the page size, and round up. `Math.ceil(array.length / page_size)`. This will give you the total amount of pages – casraf Jul 25 '23 at 09:16
23

Here's a solution with reduce():

function paginate (arr, size) {
  return arr.reduce((acc, val, i) => {
    let idx = Math.floor(i / size)
    let page = acc[idx] || (acc[idx] = [])
    page.push(val)

    return acc
  }, [])
}

let array = [1, 2, 3, 4, 5]
let page_size = 2
let pages = paginate(array, page_size)

console.log(pages)    // all pages
console.log(pages[1]) // second page

It returns an array of pages so you can get a certain page, or loop through all of them.

dodov
  • 5,206
  • 3
  • 34
  • 65
12

I saw an example above that did this correctly (kind of) and wanted to expand on it.

This was the example.

function paginate(array, page_size, page_number) {
  // human-readable page numbers usually start with 1, so we reduce 1 in the first argument
  return array.slice((page_number - 1) * page_size, page_number * page_size);
}

There are a few things wrong with this.

1.) If the page_number is 0 then it will try and set the starting split at -1 * page_size which returns an empty array. So the minimum value of the page_number attr should be 1, never anything less unless you handle that case in the function.

2.) The starting and ending index of the split are the same. Because of this, you get back an empty array. So the split should be:

return array.split(page_number * page_size, page_number * page_size + page_size)

const myArray = [1,2,3,4,5,6,7,8,9,10];

const paginateBad1 = (array, page_size, page_number) => {
  return array.slice((page_number - 1) * page_size, page_number * page_size);
};

const paginateBad2 = (array, page_size, page_number) => {
  return array.slice(page_number * page_size, page_number * page_size);
};

const paginateGood = (array, page_size, page_number) => {
  return array.slice(page_number * page_size, page_number * page_size + page_size);
};

console.log("Bad 1", paginateBad1(myArray, 2, 0));
console.log("Bad 2", paginateBad2(myArray, 2, 1));
console.log("Good", paginateGood(myArray, 2, 1));
JustinStoddard
  • 129
  • 1
  • 3
  • Running your example as is, the 'Good' returns items 3 and 4 which would be page 2 not 1 as requested by the function call. – Gichamba Nov 06 '22 at 07:34
  • The so called `paginateGood` doesn't work fine. The accepted answer works just fine. – Paschal Dec 07 '22 at 18:24
4

Another aproach that you can utilize, is using .filter, look:

const paginate = function (array, index, size) {
        // transform values
        index = Math.abs(parseInt(index));
        index = index > 0 ? index - 1 : index;
        size = parseInt(size);
        size = size < 1 ? 1 : size;

        // filter
        return [...(array.filter((value, n) => {
            return (n >= (index * size)) && (n < ((index+1) * size))
        }))]
    }

var array = [
  {id: "1"}, {id: "2"}, {id: "3"}, {id: "4"}, {id: "5"}, {id: "6"}, {id: "7"}, {id: "8"}, {id: "9"}, {id: "10"}
 ]


var transform = paginate(array, 2, 5);

console.log(transform) // [{"id":"6"},{"id":"7"},{"id":"8"},{"id":"9"},{"id":"10"}] 
4

You can use Array.filter() with the help of its second parameter (the index of the current element being processed in the array).

You'll also need the currently selected page and the number of items per page to display, so you can find the minimum and maximum index of the elements needed.

const indexMin = selectedPage * elementsPerPage;
const indexMax = indexMin + elementsPerPage;
const paginatedArray = arrayToPaginate.filter(
  (x, index) => index >= indexMin && index < indexMax
);

Updating the selectedPage and/or the elementsPerPage value will allow to return the correct items to display.

Filipe Madureira
  • 420
  • 4
  • 17
3

The use of Array#slice is the expected answer.

Here I use Symbol.iterator to create an iterable.

const arr = [1,2,3,4,5,6,7,8,9,10]

function page({arr, pageSize, pageNumber}) {
    const start = pageSize*(pageNumber-1)
    const end = pageSize*pageNumber
    return {
        *[Symbol.iterator]() {
            for(let i = start; i < arr.length && i < end; i++) {
                yield arr[i]
            }
        }
    }
}

console.log([...page({arr, pageSize: 5, pageNumber: 2})])
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
1

Hey I'm sorry I'm a bit late but we can use the Array.splice(start, end) method except this is much simpler

const page = 2
const step = 2;
const start = page * step - step;
const end = start + step;

const array = [1,2,3,4,5,6]
console.log(array.splice(start, end))
destroyer22719
  • 356
  • 2
  • 6
1

Here is another variation using Array.from with Array.slice

const paginate = (array, n) => {
  const pageSize = Math.ceil(array.length / n);
 
  return Array.from({ length: pageSize }, (_, index) => {
    const start = index * n;
    return array.slice(start, start + n);
  });
};

How to use it

Josiah Nyarega
  • 595
  • 1
  • 8
  • 11
1
 for (let pageNum = 1; pageNum <= totalPagesCount; pageNum++){
....
  const chunk = articles.slice(
        (pageNum - 1) * pageSizeNumbered,
        pageNum * pageSizeNumbered,
      );
.....
}
Mohammad Fallah
  • 976
  • 11
  • 14
0

function paginate(array, page_size, page_number) {
  // human-readable page numbers usually start with 1, so we reduce 1 in the first argument
  return array.slice((page_number - 1) * page_size, page_number * page_size);
}
var arr = [1, 2, 3, 4, 5, 6]


const options = {
        //page: parseInt(req.query.page) || 1,
        page:1,
        limit:10
        //limit: parseInt(req.query.limit) || 10,
        //customLabels: servCustomLabels,
    };


        let prev_page = 0;
         let next_page = 0;
         let h_p_p = null;
         let h_n_p = null;
         let page_count = Math.ceil((arr.length / options.limit));

        if (options.page >= page_count ){  // 2 3 
            next_page = 0;
        }        
        if(options.page >= 1 && options.page < page_count ){
            next_page = options.page + 1;
            h_n_p = true;
        }else{
            next_page = 0;
            h_n_p = false;
        }

        if(options.page <= 1 ){
            prev_page =0;
            h_p_p = false;
        }else{
            prev_page = options.page -1 ;
            h_p_p = true;
        }
        
        console.log(paginate(arr, 2, 2));
        console.log({paginator: {
                    totalDocs: arr.length,
                    perPage: options.limit,
                    pageCount: page_count,
                    currentPage: options.page,
                    //slNo: 2,
                    hasPrevPage: h_p_p,
                    hasNextPage: h_n_p,
                    prev: prev_page,
                    next: next_page
                }})
0
function paginate(arr, PerPage) {
  let map = {};
  let startPage = 1;
  arr.forEach((current) => {
    if (map[startPage] && map[startPage].length < PerPage) {
      map[startPage].push(current);
    }

    if (!map[startPage]) {
      map[startPage] = [current];
    }

    if (map[startPage] && map[startPage].length >= PerPage) {
      startPage++;
    }
  });

  return map;

}

you will find an example on this link

tifla
  • 39
  • 1
  • 4
0

The example below is using iter-ops library (I'm the author).

// our inputs...

const array = [1, 2, 3, 4, 5];

const pageSize = 2;
const pageIndex = 1;

The most efficient way is to process an array as an iterable, so you go through it once.

If you never need other pages, then the fastest way is like this:

import {pipe, skip, page} from 'iter-ops';

const p = pipe(
      array,
      skip(pageSize * pageIndex), // skip pages we don't want
      page(pageSize) // create the next page
).first;

console.log(p); //=> [3, 4]

And if you do need other pages, then you can do:

const p = pipe(
      array,
      page(pageSize), // get all pages
      skip(pageIndex) // skip pages we don't want
).first;

console.log(p); //=> [3, 4]

And in case you need to do further processing:

const i = pipe(
      array,
      page(pageSize), // get all pages
      skip(pageIndex), // skip pages we don't want
      take(1), // take just one page
      // and so on, you can process it further
);

console.log([...i]); //=> [[3, 4]]
vitaly-t
  • 24,279
  • 15
  • 116
  • 138
0

A simple solution using filter:

function paginate(array, pageIndex, pageSize) {
  const first = pageIndex * pageSize
  const last = (pageIndex * pageSize) + pageSize
  return array.filter((_, index) => {
    return first <= index && index < last
  })
}
Rashomon
  • 5,962
  • 4
  • 29
  • 67
0

I'd go with something like this;

const paginateArray =  (array, pageNumber, pageSize) => {
    const page = array.slice((pageNumber - 1) * pageSize, pageNumber * pageSize);
    return page;
};
    
const array = [1, 2, 3, 4, 5];
const pageSize = 2;
const pageNumber = 2;
    
console.log(paginateArray(array, pageNumber, pageSize));
Gichamba
  • 997
  • 12
  • 16