Fix the Broken Search Filter in a Book Catalog API
Company: Instacart
Role: Backend Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Take-home Project
Quick Answer: This question tests a backend engineer's ability to implement correct filtering logic, including case-insensitive substring matching, whitespace trimming, and multi-field search across structured data. It evaluates practical coding skills in applying boolean conditions and edge-case handling, commonly assessed in backend role interviews to gauge real-world API correctness.
Constraints
- 0 <= len(catalog) <= 10^4
- Each string field has length <= 200
- Each book has <= 20 tags
- query may be empty, may contain leading/trailing whitespace, and may contain mixed case
Examples
Input: ([{"id": 1, "title": "Clean Code", "author": "Robert Martin", "tags": ["programming"], "available": True}, {"id": 2, "title": "The Pragmatic Programmer", "author": "Andrew Hunt", "tags": ["programming", "craft"], "available": False}, {"id": 3, "title": "Cooking Basics", "author": "Jane Cook", "tags": ["food"], "available": True}], "program", False)
Expected Output: [{"id": 1, "title": "Clean Code", "author": "Robert Martin", "tags": ["programming"], "available": True}, {"id": 2, "title": "The Pragmatic Programmer", "author": "Andrew Hunt", "tags": ["programming", "craft"], "available": False}]
Explanation: Book 1 matches on its 'programming' tag; book 2 matches on both its title ('Programmer') and tag. available_only is False so the unavailable book 2 is still returned. Book 3 has no 'program' substring anywhere. Order preserved.
Input: ([{"id": 1, "title": "Clean Code", "author": "Robert Martin", "tags": ["programming"], "available": True}, {"id": 2, "title": "The Pragmatic Programmer", "author": "Andrew Hunt", "tags": ["programming", "craft"], "available": False}, {"id": 3, "title": "Cooking Basics", "author": "Jane Cook", "tags": ["food"], "available": True}], "program", True)
Expected Output: [{"id": 1, "title": "Clean Code", "author": "Robert Martin", "tags": ["programming"], "available": True}]
Explanation: Same query but available_only is True: book 2 matches the text condition but is unavailable, so it is filtered out. Only book 1 remains.
Input: ([{"id": 1, "title": "Clean Code", "author": "Robert Martin", "tags": ["programming"], "available": True}, {"id": 2, "title": "The Pragmatic Programmer", "author": "Andrew Hunt", "tags": ["programming", "craft"], "available": False}, {"id": 3, "title": "Cooking Basics", "author": "Jane Cook", "tags": ["food"], "available": True}], " COOK ", False)
Expected Output: [{"id": 3, "title": "Cooking Basics", "author": "Jane Cook", "tags": ["food"], "available": True}]
Explanation: The query ' COOK ' is trimmed to 'COOK' and lowercased to 'cook'. Book 3 matches on its author 'Jane Cook' (and also its title 'Cooking') case-insensitively. Other books have no 'cook' substring.
Input: ([{"id": 1, "title": "Clean Code", "author": "Robert Martin", "tags": ["programming"], "available": True}, {"id": 2, "title": "The Pragmatic Programmer", "author": "Andrew Hunt", "tags": ["programming", "craft"], "available": False}, {"id": 3, "title": "Cooking Basics", "author": "Jane Cook", "tags": ["food"], "available": True}], "", True)
Expected Output: [{"id": 1, "title": "Clean Code", "author": "Robert Martin", "tags": ["programming"], "available": True}, {"id": 3, "title": "Cooking Basics", "author": "Jane Cook", "tags": ["food"], "available": True}]
Explanation: Empty query imposes no text filter, so every book passes the text condition. available_only is True, so only the available books (1 and 3) are returned; book 2 is unavailable.
Input: ([], "anything", False)
Expected Output: []
Explanation: Empty catalog: there is nothing to filter, so the result is an empty list regardless of query or availability flag.
Input: ([{"id": 5, "title": "Untagged Mystery", "author": "A. Nonymous", "tags": [], "available": True}], "myst", False)
Expected Output: [{"id": 5, "title": "Untagged Mystery", "author": "A. Nonymous", "tags": [], "available": True}]
Explanation: A book with an empty tags list still matches via its title 'Untagged Mystery' (contains 'myst'). The empty tags list must not cause an error.
Input: ([{"id": 7, "title": "Deep Sea", "author": "Marina Blue", "tags": ["Ocean", "NATURE"], "available": True}], "nature", False)
Expected Output: [{"id": 7, "title": "Deep Sea", "author": "Marina Blue", "tags": ["Ocean", "NATURE"], "available": True}]
Explanation: Tag matching is case-insensitive: the stored tag 'NATURE' matches the lowercase query 'nature'. The book is returned even though the match is only on a tag.
Input: ([{"id": 8, "title": "No Match Here", "author": "Someone", "tags": ["misc"], "available": True}], "zzz", False)
Expected Output: []
Explanation: No field (title, author, or any tag) contains the substring 'zzz', so the book fails the text condition and the result is empty.
Hints
- Trim and lowercase the query ONCE before the loop. Comparing a pre-lowered query against lowered fields gives case-insensitivity cheaply.
- An empty trimmed query must pass the text condition for every book — handle it as a short-circuit so you don't accidentally treat '' as 'matches nothing'.
- The availability filter is independent of the text match: a book is returned only if it passes BOTH. Apply the availability check first to skip work.
- Preserve input order by iterating the catalog once and appending matches in place — do not sort or re-order.