81. Design a Library Management System
Design a Library Management System
Design and implement an in-memory Library Management system that caters to its registered members by cataloging and housing books that can be borrowed.
The system must support:
- Adding books to the catalog.
- Registering and unregistering users.
- Reservation management to borrow books using book ids (with FIFO waitlist).
- Fine calculation on late returns (20 rupees per delayed day after 14 days).
- (Bonus) Limiting a user to reserve only one copy of the same book.
- (Bonus) Auditing APIs:
- Given a bookId, list users currently having that book.
- Given a userId, list books currently issued to that user.
Important: Use of a DB is not allowed. The solution must use in-memory data structures only. CLI, Web application, REST API, and UI are not expected.
Terminology
- Book: Identified by
bookId, and has title, author, and copies.
- User: A registered library member identified by
userId.
- Issued / Borrowed: A copy of a book is currently with a user.
- Waitlist: A FIFO queue of users waiting to borrow a book when no copy is immediately available.
- Held Copy: A returned copy that is reserved exclusively for a specific user (
HELD_FOR,<userId>) and is not generally available.
- Day: Integer day value provided by the caller for borrow/return operations.
Book Id Generation Rules
- For each distinct book (unique by
(title, author)), the system must generate a unique bookId.
bookId format: <PREFIX><NUMBER>
PREFIX = first three letters of the author’s last name, uppercased (if last name has fewer than 3 letters, use the full last name uppercased).
NUMBER = a 4-digit sequence (zero-padded) maintained per PREFIX, starting from 1000.
- Example: author last name
Rowling → ROW1000, next ROW1001, etc.
- If the same
(title, author) is added again, the system must increase its copy count and return the same bookId.
Borrowing, Reservation, and Waitlist Rules
- Users borrow books by
bookId (assume users already know book ids).
- If at least one copy is available (not issued and not held), the book is issued to the requesting user immediately.
- If all copies are already issued (or all non-held copies are issued), the user is added to a FIFO waitlist for that
bookId.
- Held copy behavior (clarified): When a copy is returned and the waitlist is non-empty, the system must:
- dequeue the first user from the FIFO waitlist, and
- mark the returned copy as
HELD_FOR,<userId> for that dequeued user.
The held copy is not considered generally available; only that userId may successfully borrow it next.
- If the requesting user is the user for whom a copy is currently held (
HELD_FOR), the book is issued to them.
- (Bonus) One user should only be allowed to reserve/borrow one copy of the same
bookId at a time.
Fine Rules
- A user is allowed to borrow a book for 14 days.
- If returned late, fine =
20 rupees per delayed day:
borrowDuration = returnDay - borrowDay
- No fine if
borrowDuration <= 14
delayDays = borrowDuration - 14 if borrowDuration > 14
fine = delayDays * 20
Methods
1) addBook
String addBook(String title, String author, int copies)
- Adds a book entry (or increases copies for an existing
(title, author)).
- copies will be between 1 and 10000
- Returns:
BOOK_ID,<bookId> on success
INVALID_COPIES if copies <= 0
INVALID_INPUT if title or author is empty
2) registerUser
String registerUser(String userId, String name)
- Registers a new user in the library.
- Returns:
SUCCESS on success
USER_ALREADY_EXISTS if userId is already registered
INVALID_INPUT if userId or name is empty
3) unregisterUser
String unregisterUser(String userId)
- Unregisters an existing user.
- Returns:
SUCCESS on success
USER_NOT_FOUND if user does not exist
USER_HAS_ISSUED_BOOKS if user currently has any issued books
USER_IN_WAITLIST if user is in any waitlist
4) requestBorrow
String requestBorrow(String userId, String bookId, int requestDay)
- Requests to borrow a book by
bookId on requestDay.
- requestDay will be between 1 and 100,000
- Returns:
ISSUED if a copy is issued immediately to the user (including if a copy is HELD_FOR this user)
WAITLISTED,<position> if no copy is available for the user; position is 1-based in FIFO
USER_NOT_FOUND if user is not registered
BOOK_NOT_FOUND if bookId does not exist
INVALID_DAY if requestDay < 0
ALREADY_ISSUED_TO_USER if user already has this bookId issued (bonus behavior; if not implemented, you may omit this check)
ALREADY_WAITLISTED if user is already in the waitlist for this bookId
- Note: Since the FIFO head is dequeued when a copy becomes
HELD_FOR them, they will not be considered “already waitlisted” at that time.
5) returnBook
String returnBook(String userId, String bookId, int returnDay)
- Returns an issued book copy and calculates fine if returned late.
- returnDay will be between 1 and 100,000
- Returns:
RETURNED,<fine> on success (fine can be 0)
USER_NOT_FOUND if user is not registered
BOOK_NOT_FOUND if bookId does not exist
NOT_ISSUED_TO_USER if that user does not currently have this book issued
INVALID_DAY if returnDay < 0 or returnDay < borrowDay
- On a successful return, if a waitlist exists, the system must dequeue the FIFO head and mark the returned copy as
HELD_FOR,<userId> for that dequeued user.
6) usersHavingBook
List<String> usersHavingBook(String bookId)
- Auditing API: given a
bookId, returns a list of userIds currently issued that book.
- Output rules:
- Return
[] if bookId does not exist or no one currently has it.
- Sort userIds in ascending lexicographic order for deterministic output.
7) booksIssuedToUser
List<String> booksIssuedToUser(String userId)
- Auditing API: given a
userId, returns a list of bookIds currently issued to the user.
- Output rules:
- Return
[] if userId does not exist or user has no issued books.
- Sort bookIds in ascending lexicographic order for deterministic output.
Constraints
1 <= title.length() <= 200
1 <= author.length() <= 200 (author includes first/last name; last token is treated as last name for id prefix)
1 <= userId.length() <= 50
1 <= name.length() <= 200
1 <= copies <= 100000
0 <= requestDay, returnDay <= 1000000000
- All operations must be supported using in-memory data structures (no database).
- Waitlist ordering must be FIFO.
Examples
- Note: Each example below assumes a fresh empty system (no data carries over between examples).
Example 1: Add books (including more copies), register users, and issue immediately
addBook(title="Harry Potter and the Sorcerer's Stone", author="J K Rowling", copies=2) → BOOK_ID,ROW1000
addBook(title="Harry Potter and the Sorcerer's Stone", author="J K Rowling", copies=1) → BOOK_ID,ROW1000 (same (title, author); copies increase)
registerUser(userId="U1", name="Alice") → SUCCESS
registerUser(userId="U2", name="Bob") → SUCCESS
requestBorrow(userId="U1", bookId="ROW1000", requestDay=1) → ISSUED
requestBorrow(userId="U2", bookId="ROW1000", requestDay=1) → ISSUED (another copy is available)
usersHavingBook(bookId="ROW1000") → ["U1","U2"]
booksIssuedToUser(userId="U1") → ["ROW1000"]
Example 2: Waitlist behavior, dequeue + HELD_FOR for FIFO head, and late fine on return
registerUser(userId="U1", name="Alice") → SUCCESS
registerUser(userId="U2", name="Bob") → SUCCESS
registerUser(userId="U3", name="Charlie") → SUCCESS
addBook(title="Clean Code", author="Robert C Martin", copies=1) → BOOK_ID,MAR1000
requestBorrow(userId="U1", bookId="MAR1000", requestDay=5) → ISSUED
requestBorrow(userId="U2", bookId="MAR1000", requestDay=6) → WAITLISTED,1 (no copy available)
requestBorrow(userId="U3", bookId="MAR1000", requestDay=6) → WAITLISTED,2
returnBook(userId="U1", bookId="MAR1000", returnDay=10) → RETURNED,0 (borrowDuration = 10 - 5 = 5 ≤ 14; FIFO head U2 is dequeued and copy becomes HELD_FOR,U2)
requestBorrow(userId="U3", bookId="MAR1000", requestDay=10) → ALREADY_WAITLISTED (still waiting; cannot borrow a copy held for U2)
requestBorrow(userId="U2", bookId="MAR1000", requestDay=10) → ISSUED (copy is HELD_FOR,U2)
returnBook(userId="U2", bookId="MAR1000", returnDay=30) → RETURNED,120 (borrowDuration = 30 - 10 = 20; delayDays = 20 - 14 = 6; fine = 6 * 20 = 120; FIFO head U3 is dequeued and copy becomes HELD_FOR,U3)
requestBorrow(userId="U3", bookId="MAR1000", requestDay=30) → ISSUED (copy is HELD_FOR,U3)
usersHavingBook(bookId="MAR1000") → ["U3"]
booksIssuedToUser(userId="U2") → [] (book returned)
Example 3: Fine calculation on late return
registerUser(userId="U1", name="Alice") → SUCCESS
addBook(title="The Hobbit", author="J R R Tolkien", copies=1) → BOOK_ID,TOL1000
requestBorrow(userId="U1", bookId="TOL1000", requestDay=1) → ISSUED
returnBook(userId="U1", bookId="TOL1000", returnDay=20) → RETURNED,100 (borrowDuration = 20 - 1 = 19; delayDays = 19 - 14 = 5; fine = 5 * 20 = 100)
usersHavingBook(bookId="TOL1000") → [] (no active issue)
booksIssuedToUser(userId="U1") → [] (book returned)