title: Pagination algorithm: display ranked search results for unique eCommerce merchants
body: <p>Hi Stack Overflow community,</p>
  
  <p>I am building a search functionality for an eCommerce website and looking for the most <em>optimal</em> <strong>Pagination algorithm</strong> that satisfies several requirements.</p>
  
  <p>When a user submits a search product query on our site, we return a list of products scored according to their specified preferences. Search results are then presented over several web pages. Because multiple listings can belong to the same merchant, we want to ensure that a merchant does not dominate the results in a single page.</p>
  
  <ol>
  <li>Results are sorted by <strong>descending</strong> order of score. </li>
  <li>Given the number of <strong>results per page</strong>, we divide the result set into separate pages (<strong>paginate</strong>) while having a merchant appear at most once on each page. </li>
  <li>If a page cannot be filled with only unique merchants, <strong><em>then</em></strong> the page is padded with the remaining product entries while preserving the <strong>initial ordering</strong>.</li>
  </ol>
  
  <p>For example:
      // results to display per page
      let numPerPage = 4; </p>
  
  <pre><code>// results array of csv, comma-delimited strings with format:
  // merchant_id,listing_id,score,description
  
  let results = [
   "5,90,500.1,description text",  
   "4,51,495.8,description text",  
   "8,72,490,description text",  
   "5,89,485.4,description text",
   "5,80,480,description text",
   "9,18,478.1,description text",  
   "6,50,475.9,description text",  
   "5,56,472,description text",  
   "7,12,470.5,description text",  
   "5,5,465,description text",
   "6,10,450,description text"
  ]
  
  // Paginate function output: array of strings representing paginated results, 
  // with an empty String separator after each page, except the last page.
  let output = [
   "5,90,500.1,description text",  
   "4,51,495.8,description text",  
   "8,72,490,description text", 
   "9,18,478.1,description text",
   "", // this is a page separator
   "5,89,485.4,description text",
   "6,50,475.9,description text",  
   "7,12,470.5,description text",
   "6,10,450,description text",
   "", // this is a page separator
   "5,80,480,description text",
   "5,56,472,description text",  
   "5,5,465,description text"]
  </code></pre>
  
  <p>The following code has been written so far:</p>
  
  <pre><code>function paginate(numPerPage, results) {
   // first map : transform into array of arrays
   // second map : transform into array of objects w/ key-value pairs
   let hash = {};
   let output = [];
   output = results.map(listing =&gt; listing.split(',')).map((listing, i) =&gt; {
      return {
        "index": i,
        "merchant_id": listing[0],
        "listing_id": listing[1],
        "score": listing[2],
        "description": listing[3]
      }
    });
    // sort the listings by descending score.
    output = output.sort((a,b) =&gt; b.score-a.score);
    console.log(output);
   // next, create a hash of merchant: array of listings
   // while results per page &lt; numPerPage ...
   // ... optimal pagination algorithm in progress..
   }
  </code></pre>
  
  <p>I'm thinking of creating a hash of <code>"merchant": [array of that merchant's listings]</code>, and <code>shift()</code> the first value of the merchant arrays as we progress. But the algorithm gets interesting when into account <strong>unique</strong> merchant preference per page requirement. In addition, the algorithm I came up wasn't very optimal.</p>
  
  <p>Would truly appreciate any help!</p>