PracHub
QuestionsCoachesLearningGuidesInterview Prep

Quick Overview

This question tests a candidate's ability to design and implement a tree-structured data model with efficient CRUD operations in memory. It evaluates practical object-oriented design, recursive traversal, cycle detection, and algorithmic efficiency under scale constraints — core competencies assessed in software engineering coding interviews.

  • medium
  • Goldman Sachs
  • Coding & Algorithms
  • Software Engineer

Design a Workspace Resource Manager

Company: Goldman Sachs

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

# Design a Workspace Resource Manager You are building the in-memory core of a workspace application. A workspace organizes **resources** in a tree. There are two kinds of resource: - A **folder**, which can contain other resources (folders or files). - A **file**, which is a leaf and cannot contain anything. Every resource has a unique string `id` (you assign it when the resource is created), a human-readable `name`, and a `type`. Resources live either at the **home level** (the implicit root of the workspace, which is itself a folder) or inside exactly one parent folder. Implement a class `ClayWorkspace` that supports creating, listing, deleting, and moving resources. ## API to implement ```python from enum import Enum from typing import List, Optional class ResourceType(Enum): FOLDER = "folder" FILE = "file" class ClayWorkspace: def __init__(self) -> None: ... def create_resource( self, resource_name: str, resource_type: ResourceType, parent_folder_id: Optional[str] = None, ) -> str: """ Create a new resource named `resource_name` of type `resource_type` inside the folder identified by `parent_folder_id`. If `parent_folder_id` is None, the resource is created at the home level (the workspace root). Returns the unique id assigned to the newly created resource. """ ... def list_resources(self, folder_id: Optional[str] = None) -> List[str]: """ Return the ids of the resources that are DIRECT children of the folder identified by `folder_id` (non-recursive). If `folder_id` is None, return the ids of the resources at the home level. The returned ids must be ordered by the time their resources were created (oldest first). """ ... def delete_resource(self, resource_id: str) -> None: """ Delete the resource identified by `resource_id`. If it is a folder, also delete every resource inside it, recursively (all descendants). After this call, none of the deleted ids may appear in any listing. """ ... def move_resource( self, resource_id: str, destination_folder_id: Optional[str] = None, ) -> None: """ Move the resource identified by `resource_id` so that its new parent is the folder identified by `destination_folder_id`. If `destination_folder_id` is None, move it to the home level. The resource keeps its id, its name, and (if it is a folder) all of its descendants; only its parent changes. """ ... ``` ## Rules and validation - `create_resource` may only create a resource inside a **folder**. If `parent_folder_id` refers to a resource that is not a folder, raise `ValueError`. (Home level is always a valid folder target.) - For `list_resources`, `delete_resource`, and `move_resource`, if the referenced id does not exist, raise `KeyError`. If `list_resources` or `move_resource`'s destination refers to a resource that exists but is not a folder, raise `ValueError`. - `move_resource` must reject any move that would create a cycle. Specifically, a folder may not be moved into itself or into any of its own descendants; raise `ValueError` in that case. - Two resources may share the same `name` (names are not unique); ids are the only unique key. ## Constraints - Resource ids you generate must be unique strings for the lifetime of the workspace and must not be reused after a delete. - There may be up to $10^5$ resources and up to $10^5$ total operations. `create_resource`, `list_resources` (excluding the cost of returning the list), `delete_resource`, and `move_resource` should each run efficiently — aim for time proportional to the number of resources actually touched by the operation, not the size of the whole workspace. ## Example ```python ws = ClayWorkspace() docs = ws.create_resource("Docs", ResourceType.FOLDER) # folder at home notes = ws.create_resource("notes.txt", ResourceType.FILE, docs) # file inside Docs sub = ws.create_resource("Drafts", ResourceType.FOLDER, docs) # folder inside Docs draft = ws.create_resource("draft.txt", ResourceType.FILE, sub) # file inside Drafts ws.list_resources() # -> [docs] (home level, one folder) ws.list_resources(docs) # -> [notes, sub] (direct children of Docs, in creation order) ws.move_resource(notes) # move notes.txt to home level ws.list_resources() # -> [docs, notes] ws.list_resources(docs) # -> [sub] ws.delete_resource(docs) # deletes Docs, Drafts, and draft.txt ws.list_resources() # -> [notes] ```

Quick Answer: This question tests a candidate's ability to design and implement a tree-structured data model with efficient CRUD operations in memory. It evaluates practical object-oriented design, recursive traversal, cycle detection, and algorithmic efficiency under scale constraints — core competencies assessed in software engineering coding interviews.

Build the in-memory core of a workspace that organizes **resources** in a tree. A resource is either a **folder** (can contain other resources) or a **file** (a leaf). Every resource has a unique string `id` you assign on creation, a `name`, and a `type`. Resources live at the **home level** (the implicit root folder) or inside exactly one parent folder. Implement a class `ClayWorkspace` with: - `create_resource(name, type, parent_folder_id=None) -> id` — create a resource inside `parent_folder_id` (or at home if `None`) and return its new unique id. May only create inside a **folder**; if `parent_folder_id` refers to a non-folder, raise `ValueError`. - `list_resources(folder_id=None) -> List[id]` — return the ids of the **direct** children of `folder_id` (or of home if `None`), ordered oldest-created first. - `delete_resource(resource_id)` — delete the resource and, if it is a folder, all of its descendants recursively. - `move_resource(resource_id, destination_folder_id=None)` — reparent the resource under `destination_folder_id` (or home if `None`), keeping its id, name, and descendants. **Validation:** for `list`/`delete`/`move`, a non-existent referenced id raises `KeyError`; a `list_resources`/`move` destination that exists but is not a folder raises `ValueError`. `move_resource` must reject any move of a folder into itself or into one of its own descendants (`ValueError`). Names are not unique; ids are the only unique key, are never reused after delete, and each operation should run in time proportional to the resources it actually touches. **Harness note:** the executable form of this problem is `solution(operations)`, which replays a list of operations against a fresh `ClayWorkspace` and returns one result per operation. Each operation is a list whose first element is the op name. Resources are referenced by the **index of the create op** that produced them (an integer), so results are deterministic regardless of how you generate ids: - `['create', name, type, parent_ref]` -> appends the new resource's index, returns that index - `['list', folder_ref]` -> returns the list of resource-indices that are direct children - `['delete', ref]` -> returns `None` - `['move', ref, dest_ref]` -> returns `None` - `['create_err', name, type, parent_ref]` / `['move_err', ref, dest_ref]` -> returns the raised exception name as a string (`'ValueError'`/`'KeyError'`) or `'NO_ERROR'` `type` is the string `'folder'` or `'file'`; a `*_ref` of `None` means the home level, otherwise it is the integer index of an earlier create.

Constraints

  • Up to 10^5 resources and up to 10^5 total operations.
  • Ids are unique strings for the lifetime of the workspace and are never reused after a delete.
  • Resource names are not unique; the id is the only unique key.
  • A folder may not be moved into itself or any of its descendants.
  • In the harness, resources are referenced by the integer index of the create op that produced them; a ref of None means the home level.

Examples

Input: ([['create', 'Docs', 'folder', None], ['create', 'notes.txt', 'file', 0], ['create', 'Drafts', 'folder', 0], ['create', 'draft.txt', 'file', 2], ['list', None], ['list', 0], ['move', 1, None], ['list', None], ['list', 0], ['delete', 0], ['list', None]],)

Expected Output: [0, 1, 2, 3, [0], [1, 2], None, [0, 1], [2], None, [1]]

Explanation: The worked example from the prompt: build Docs/notes.txt/Drafts/draft.txt, list home then Docs, move notes.txt to home (home becomes [Docs, notes], Docs becomes [Drafts]), then delete Docs (and Drafts + draft.txt recursively), leaving home = [notes].

Input: ([['list', None]],)

Expected Output: [[]]

Explanation: Edge case: listing the home level of a brand-new workspace returns an empty list.

Input: ([['create', 'a', 'file', None], ['create_err', 'b', 'file', 0], ['create', 'F', 'folder', None], ['create', 'c', 'file', 1]],)

Expected Output: [0, 'ValueError', 1, 2]

Explanation: Creating 'b' inside resource 0 ('a', a file) raises ValueError because you can only create inside a folder; creating 'c' inside resource 1 ('F', a folder) succeeds and gets index 2.

Input: ([['create', 'A', 'folder', None], ['create', 'B', 'folder', 0], ['create', 'C', 'folder', 1], ['move_err', 0, 2], ['move_err', 0, 0], ['move', 1, None], ['list', None], ['list', 0]],)

Expected Output: [0, 1, 2, 'ValueError', 'ValueError', None, [0, 1], []]

Explanation: Chain A>B>C. Moving A into C (its own descendant) and moving A into itself both raise ValueError. Moving B to home succeeds, so home = [A, B] and A now has no direct children (C went with B).

Input: ([['create', 'root', 'folder', None], ['create', 'f1', 'file', 0], ['create', 'f2', 'file', 0], ['create', 'f3', 'file', 0], ['move', 2, None], ['list', 0], ['list', None]],)

Expected Output: [0, 1, 2, 3, None, [1, 3], [0, 2]]

Explanation: Removing a middle child (f2) from a folder preserves the creation order of the remaining children: root keeps [f1, f3]; home gains f2 after root.

Input: ([['create', 'X', 'folder', None], ['create', 'Y', 'folder', 0], ['create', 'leaf', 'file', 1], ['delete', 1], ['list', 0], ['list', None]],)

Expected Output: [0, 1, 2, None, [], [0]]

Explanation: Recursive delete: deleting folder Y also removes its child 'leaf', so X has no children and home still has only X.

Hints

  1. Store one record per resource in a dict keyed by id, holding its name, type, parent pointer, and an ordered list of child ids. Appending to a per-folder list naturally preserves creation order for list_resources.
  2. For delete, detach the node from its parent's child list, then iterate its subtree with an explicit stack/queue, removing each id from the dict so deleted ids vanish from every future listing.
  3. For move, detect cycles by walking from the destination up through parent pointers: if you reach the resource being moved, the destination is inside it, so reject the move.
Last updated: Jun 24, 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
  • AI Coding 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

  • Implement an Integer Hash Map - Goldman Sachs
  • Solve string and hashmap coding tasks - Goldman Sachs (medium)
  • Implement a String Deque - Goldman Sachs (easy)
  • Find first non-repeating character index - Goldman Sachs (nan)
  • Solve string and hashmap interview tasks - Goldman Sachs (medium)