PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

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.

  • hard
  • Circle
  • Coding & Algorithms
  • Software Engineer

Implement Recipe Storage CRUD

Company: Circle

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: hard

Interview Round: Technical Screen

Implement an in-memory `RecipeStore` that supports CRUD operations for recipes. Each recipe contains: - `id`: a string identifier in the form `recipe<n>` - `name`: the recipe name - `ingredients`: a list of strings - `steps`: a list of strings Implement these methods: - `create(name, ingredients, steps) -> string` - `get(recipeId) -> Recipe | null` - `update(recipeId, updatedRecipe) -> bool` - `delete(recipeId) -> bool` Required behavior: 1. IDs are assigned sequentially starting from 1 and exposed as `recipe1`, `recipe2`, and so on. 2. `get`, `update`, and `delete` receive the id as a string such as `recipe2`. You must parse the numeric suffix to find the stored recipe. If the format is invalid, the operation fails. 3. Recipe names are unique in a case-insensitive way. For example, `Pasta` and `pAsTa` should be treated as the same logical name, so both cannot exist at the same time. 4. Preserve the stored casing of the name when returning a recipe. If a recipe was created as `PaSta`, `get` must return `PaSta`. 5. `update` succeeds only if `recipeId` matches `updatedRecipe.id`. 6. During `update`, the name may change only by letter case. For example, `PaSta` to `pasta` is allowed, but `PaSta` to `Noodles` is not. 7. If a case-only rename is accepted, future reads should return the new casing. 8. Updating or deleting a non-existent recipe should fail. Design appropriate data structures and implement the methods so common operations are efficient.

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.

Process a sequence of operations against an in-memory `RecipeStore`. Each recipe has four fields: `id`, `name`, `ingredients`, and `steps`. A recipe id is exposed as strings like `recipe1`, `recipe2`, and so on. Return the result of every operation in order. Operation forms are: `('create', name, ingredients, steps)`, `('get', recipeId)`, `('update', recipeId, updatedRecipe)`, and `('delete', recipeId)`. Rules: (1) successful creates receive sequential ids starting at `recipe1`; failed creates do not consume an id, (2) `get`, `update`, and `delete` must parse the numeric suffix from the id and fail if the format is invalid, (3) recipe names are unique case-insensitively, so `Pasta` and `pAsTa` cannot both exist, (4) preserve the stored casing of the name when returning a recipe, (5) `update` succeeds only if `recipeId == updatedRecipe['id']`, (6) during `update`, the name may change only by letter case, such as `PaSta` to `pasta`, but not to a different logical name, (7) if a case-only rename succeeds, future reads must return the new casing, and (8) updating or deleting a non-existent recipe fails. For `create`, return the new id on success or `None` on failure. For `get`, return the recipe dictionary or `None`. For `update` and `delete`, return `True` or `False`.

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`.

Input: [('create', 'Salad', ['lettuce'], ['mix']), ('get', 'rec1'), ('get', 'recipe0'), ('update', 'recipe2', {'id': 'recipe2', 'name': 'Salad', 'ingredients': ['lettuce', 'oil'], 'steps': ['mix']}), ('delete', 'recipe999')]

Expected Output: ['recipe1', None, None, False, False]

Explanation: `rec1` and `recipe0` are invalid ids, and `recipe2`/`recipe999` do not exist.

Input: [('create', 'Bread', ['flour'], ['bake']), ('update', 'recipe1', {'id': 'recipe2', 'name': 'bread', 'ingredients': ['flour', 'water'], 'steps': ['mix', 'bake']}), ('update', 'recipe1', {'id': 'recipe1', 'name': 'Toast', 'ingredients': ['flour'], 'steps': ['bake']}), ('get', 'recipe1')]

Expected Output: ['recipe1', False, False, {'id': 'recipe1', 'name': 'Bread', 'ingredients': ['flour'], 'steps': ['bake']}]

Explanation: The first update fails because the ids do not match. The second fails because changing `Bread` to `Toast` is not a case-only rename.

Input: [('create', 'Cake', ['flour', 'sugar'], ['mix', 'bake']), ('delete', 'recipe1'), ('create', 'cAkE', ['flour'], ['bake']), ('get', 'recipe2')]

Expected Output: ['recipe1', True, 'recipe2', {'id': 'recipe2', 'name': 'cAkE', 'ingredients': ['flour'], 'steps': ['bake']}]

Explanation: Deleting a recipe frees its case-insensitive name, so the same logical name can be created again later with the next id.

Input: [('create', 'Tea', [], []), ('get', 'recipe1'), ('update', 'recipe1', {'id': 'recipe1', 'name': 'TEA', 'ingredients': [], 'steps': ['steep']}), ('get', 'recipe1')]

Expected Output: ['recipe1', {'id': 'recipe1', 'name': 'Tea', 'ingredients': [], 'steps': []}, True, {'id': 'recipe1', 'name': 'TEA', 'ingredients': [], 'steps': ['steep']}]

Explanation: Empty ingredient and step lists are valid, and a case-only rename updates the stored casing returned by later reads.

Input: []

Expected Output: []

Explanation: With no operations, the result is an empty list.

Hints

  1. 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.
  2. 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.
Last updated: Jun 11, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Design payment scheduler with cancel and top-K outgoing - Circle (Medium)
  • Implement a simplified multi-level banking system - Circle (Medium)
  • Implement cheapest itinerary with date filters - Circle (Medium)