Implement Recipe Storage CRUD
Company: Circle
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
Quick Answer: This question evaluates the ability to design and implement in-memory data structures and APIs for CRUD operations, covering skills such as ID generation and parsing, string handling for case-insensitive uniqueness constraints, data modeling, and preserving original value casing.
Constraints
- 0 <= len(operations) <= 2 * 10^5
- A valid recipe id is exactly `'recipe'` followed by a positive integer with no leading zeros, such as `'recipe7'`
- The total number of ingredient and step strings processed is at most 2 * 10^5
Examples
Input: [('create', 'Pasta', ['noodles', 'salt'], ['boil', 'serve']), ('get', 'recipe1'), ('update', 'recipe1', {'id': 'recipe1', 'name': 'pasta', 'ingredients': ['noodles', 'salt', 'oil'], 'steps': ['boil', 'mix', 'serve']}), ('get', 'recipe1'), ('delete', 'recipe1'), ('get', 'recipe1')]
Expected Output: ['recipe1', {'id': 'recipe1', 'name': 'Pasta', 'ingredients': ['noodles', 'salt'], 'steps': ['boil', 'serve']}, True, {'id': 'recipe1', 'name': 'pasta', 'ingredients': ['noodles', 'salt', 'oil'], 'steps': ['boil', 'mix', 'serve']}, True, None]
Explanation: The recipe is created, read, updated with a case-only rename, read again with the new casing, deleted, and then no longer found.
Input: [('create', 'PaSta', ['a'], ['b']), ('create', 'pAsTa', ['x'], ['y']), ('create', 'Soup', ['water'], ['heat']), ('get', 'recipe2')]
Expected Output: ['recipe1', None, 'recipe2', {'id': 'recipe2', 'name': 'Soup', 'ingredients': ['water'], 'steps': ['heat']}]
Explanation: The second create fails because the name already exists ignoring case. Since failed creates do not consume ids, `Soup` becomes `recipe2`.
Hints
- Use two hash maps: one from numeric recipe id to the stored recipe, and one from a normalized name (such as `casefold()`) to the numeric id.
- Before any `get`, `update`, or `delete`, validate and parse the id string. During `update`, compare the old and new names after normalization to allow only case-only changes.