AlgoMaster Logo
AlgoMasterDesign Paginated Catalogmedium

Design Paginated Catalog

medium

Imagine browsing a large storefront where loading every product up front would be wasteful. Each shopper should pull in one page at a time, only when the current page can no longer answer the next question. Two shoppers may use different page sizes and should never share paging state.

Product and ProductCatalog are already implemented for you. Your task is to implement only the Iterator pattern participants:

  • ProductCursor, the common contract for traversal and paging diagnostics.
  • PaginatedCursor, the lazy iterator that owns its current page, offset, page position, fetch count, and transcript.

The cursor is constructed with the catalog and a page size. It can call the provided page(offset, limit) method to fetch a window and totalCount() to see whether more products exist. Opening a cursor must not fetch anything; the first call to hasNext() or next() that needs data triggers the first fetch.

The pre-implemented ProductCatalog exposes the following API to the tests:

  • ProductCatalog() creates a catalog holding no products and no cursors.
  • boolean addProduct(int id, String name, double price) appends a product and returns true, or returns false once 30 products are stored.
  • int totalCount() returns how many products are stored.
  • int openCursor(int pageSize) opens a cursor that reads pageSize products per page and returns its id, counting from 1. A page size below 1 opens nothing and returns -1.
  • boolean hasNext(int cursorId) returns whether a product is left, fetching the next page first if the current one is used up. An id that was never handed out returns false.
  • String next(int cursorId) returns the next product and moves the cursor along, fetching a page first if the current one is used up. A cursor with nothing left returns "END", and an unknown id returns "NONE".
  • int pagesLoaded(int cursorId) returns how many pages that cursor has fetched, or -1 for an unknown id.
  • String transcript(int cursorId) returns that cursor's fetch log. Each fetch adds one Loading page N... entry, numbered from 1 for that cursor, and the entries are joined by " | ". A cursor that has fetched nothing returns "EMPTY", and an unknown id returns "NONE".

A product reads as Product{id=1, name='Laptop', price=999.99}. Formatting to exactly two decimal places is already handled by the provided Product class.

A page is fetched when a call needs a product the cursor does not already hold. Opening a cursor fetches nothing, and a cursor over an empty catalog never fetches. The final page may be shorter than pageSize.

Two cursors over the same catalog read at their own pace, each with its own page, fetch count, and transcript. Unknown cursor ids and invalid page sizes are handled by the provided catalog code.

Example 1:

Input:

Output:

Explanation: Opening the cursor fetches nothing, so the transcript is empty and the fetch count is zero. Page 1 arrives with the first next, and page 2 only when a third next runs past the end of page 1.

Example 2:

Input:

Output:

Explanation: Two cursors read the same four products at different page sizes. After three products each, the one reading two at a time has fetched two pages while the one reading three at a time has fetched one.

Constraints

  • 1 <= id <= 1000
  • 1 <= name.length <= 40
  • 0.0 <= price <= 10000.0, given with at most two decimal places
  • -5 <= pageSize <= 50
  • At most 30 products are stored in one catalog.
  • At most 100 calls in total are made across all methods.

How the design is graded

needs 7/10 to pass
  • Iterator separation

    Full marks when the paging walk is its own class behind one contract, the catalog hands out a page for an offset and a limit, and the catalog answers by delegating to a cursor it looks up by id. Lose points heavily when the current page, the offset or the fetch count is a field on `ProductCatalog`.

  • Pages arrive only when needed

    Full marks when opening a cursor fetches nothing, the first page arrives on the first call that needs a product, and each later page arrives on the call that first runs past the end of the one before it. Lose points when the cursor fetches a page as it is created, or when it reads the whole catalog up front.

  • Structure and naming

    Full marks when prices print to exactly two decimal places, a final short page ends the walk without another fetch, an empty catalog fetches nothing at all, a page size below `1` returns `-1` without opening anything, and an exhausted cursor returns `END`. Lose points for printing to stdout.

Passing every test is not enough on its own. A submission is accepted only when the design also clears the bar.

Hints

Loading...
CallReturns
new ProductCatalog()null
addProduct(1, "Laptop", 999.99)true
addProduct(2, "Mouse", 29.99)true
addProduct(3, "Keyboard", 79.99)true
addProduct(4, "Monitor", 349.99)true
addProduct(5, "Headphones", 149.99)true
totalCount()5
openCursor(2)1
pagesLoaded(1)0
transcript(1)"EMPTY"
next(1)"Product{id=1, name='Laptop', price=999.99}"
next(1)"Product{id=2, name='Mouse', price=29.99}"
pagesLoaded(1)1
next(1)"Product{id=3, name='Keyboard', price=79.99}"
pagesLoaded(1)2
transcript(1)"Loading page 1... | Loading page 2..."

Opening the cursor fetches nothing, so the transcript is empty and the fetch count is zero. Page 1 arrives with the first `next`, and page 2 only when a third `next` runs past the end of page 1.

Run checks these cases. Submit also runs a larger hidden set.