Katkat

Build an In-Memory File Vault with Duplicate-Name Handling

HarveySoftware Engineer · Onsite

Last updated

Design an in-memory hierarchical file vault supporting automatic directory creation, lexicographical directory listings, and duplicate-name resolution using the smallest available numeric suffix.

Problem Statement

Implement an in-memory hierarchical file vault that manages files and directories. The vault must automatically create any missing intermediate directories when a file is added. If a file with the target name already exists in the destination directory, the vault must resolve the collision by finding the smallest positive integer k >= 1 such that appending (k) to the base name produces an unused filename.

Implement the FileVault class:

  • FileVault(): Initializes the vault with an empty root directory /.
  • add_file(path: str) -> str: Inserts a file at the given absolute path, creating any missing parent directories along the way. Returns the final absolute path of the created file after resolving any name collisions.
  • list_dir(path: str) -> list[str]: Returns a list of all immediate child entries (both subdirectories and files) directly located in path, sorted in lexicographical order (ASCII). If path does not exist or refers to a file, return an empty list.

Name Collision & Extension Rules

For a target filename:

  1. If a dot . exists at an index greater than 0, the extension is defined as everything from the last . to the end, and the base name is everything preceding it (e.g., "archive.tar.gz" has base "archive.tar" and extension ".gz").
  2. If there is no dot, or if the only dot is at index 0 (e.g., ".env"), the extension is empty "" and the base name is the entire filename.
  3. If the resolved target filename already exists in the target directory, format candidate names as f"{base} ({k}){ext}" for k = 1, 2, 3, ... and assign the first available name.

Interface

class FileVault:
    def __init__(self):
        pass

    def add_file(self, path: str) -> str:
        pass

    def list_dir(self, path: str) -> list[str]:
        pass

Constraints

  • All paths are absolute paths starting with /.
  • Paths contain valid ASCII characters, without consecutive slashes (e.g., //) or trailing slashes (except the root /).
  • Total operations across add_file and list_dir will not exceed 10,000.
  • Directory and file names do not contain /.
  • A file and directory will not share the exact same name within the same parent directory.

Examples

Example 1

vault = FileVault()

vault.add_file("/docs/report.pdf")
# Returns: "/docs/report.pdf"

vault.add_file("/docs/report.pdf")
# Returns: "/docs/report (1).pdf"

vault.add_file("/docs/report.pdf")
# Returns: "/docs/report (2).pdf"

vault.list_dir("/docs")
# Returns: ["report (1).pdf", "report (2).pdf", "report.pdf"]

Example 2

vault = FileVault()

vault.add_file("/home/.bashrc")
# Returns: "/home/.bashrc"

vault.add_file("/home/.bashrc")
# Returns: "/home/.bashrc (1)"

vault.add_file("/home/notes (2).txt")
# Returns: "/home/notes (2).txt"

vault.add_file("/home/notes.txt")
# Returns: "/home/notes.txt"

vault.add_file("/home/notes.txt")
# Returns: "/home/notes (1).txt" (smallest available k >= 1)

vault.list_dir("/home")
# Returns: [".bashrc", ".bashrc (1)", "notes (1).txt", "notes (2).txt", "notes.txt"]

Hints

Hint 1

Model the filesystem as a tree of directory nodes, where each node stores a set/map of child directories and a set/map of files.

Hint 2

Be careful when splitting the filename into base and extension: find the last occurrence of '.' and only treat it as an extension delimiter if its index is strictly greater than 0.

Hint 3

To find the smallest available suffix, check `k = 1, 2, ...` against a hash set of existing names in that specific directory until an unused name is found.

Follow-up questions

  • How would you optimize the search for the smallest `k` if there are millions of duplicate additions for the same base name?
  • How would your design change if you needed to support moving or renaming directories (`mv /a /b`) efficiently without rebuilding entire paths?
  • How would you make this file vault thread-safe for concurrent read and write operations without creating a global lock bottleneck?