Offset/limit to page/size conversion Offset/limit to page/size conversion database database

Offset/limit to page/size conversion


As pointed out by @usr, in most variants of this problem (whether querying a database, API, or some other entity) it is preferable to reduce the number of calls as much as possible, since returning a few extra rows is almost always cheaper than issuing a separate call. The following PageSizeConversion algorithm will always find a single call that returns the fewest records possible (which is exactly how it performs the search). There may be some extra records returned at the beginning (headWaste) or end (tailWaste) of the data set, required to fit the data set within a single page. The algorithm is implemented in Javascript here, but it is easy to port to any language.

function PageSizeConversion(offset, limit) {  var window, leftShift;  for (window = limit; window <= offset + limit; window++) {    for (leftShift = 0; leftShift <= window - limit; leftShift++) {      if ((offset - leftShift) % window == 0) {        this.pageSize = window;        this.page = (offset - leftShift) / this.pageSize;        this.headWaste = leftShift;        this.tailWaste = ((this.page + 1) * this.pageSize) - (offset + limit);        return;      }    }  }}var testData = [  {"offset": 0,"limit": 10,"expectedPage": 0,"expectedSize": 10,"expectedHeadWaste": 0,"expectedTailWaste": 0},  {"offset": 2,"limit": 1,"expectedPage": 2,"expectedSize": 1,"expectedHeadWaste": 0,"expectedTailWaste": 0},  {"offset": 2,"limit": 2,"expectedPage": 1,"expectedSize": 2,"expectedHeadWaste": 0,"expectedTailWaste": 0},  {"offset": 5,"limit": 3,"expectedPage": 1,"expectedSize": 4,"expectedHeadWaste": 1,"expectedTailWaste": 0},  {"offset": 3,"limit": 5,"expectedPage": 0,"expectedSize": 8,"expectedHeadWaste": 3,"expectedTailWaste": 0},  {"offset": 7,"limit": 3,"expectedPage": 1,"expectedSize": 5,"expectedHeadWaste": 2,"expectedTailWaste": 0},  {"offset": 1030,"limit": 135,"expectedPage": 7,"expectedSize": 146,"expectedHeadWaste": 8,"expectedTailWaste": 3},];describe("PageSizeConversion Tests", function() {  testData.forEach(function(testItem) {    it("should return correct conversion for offset " + testItem.offset + " limit " + testItem.limit, function() {      conversion = new PageSizeConversion(testItem.offset, testItem.limit);      expect(conversion.page).toEqual(testItem.expectedPage);      expect(conversion.pageSize).toEqual(testItem.expectedSize);      expect(conversion.headWaste).toEqual(testItem.expectedHeadWaste);      expect(conversion.tailWaste).toEqual(testItem.expectedTailWaste);    });  });});// load jasmine htmlReporter(function() {  var env = jasmine.getEnv();  env.addReporter(new jasmine.HtmlReporter());  env.execute();}());
<script src="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script><script src="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script><link href="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css" rel="stylesheet" /><title>Jasmine Spec Runner</title>