Design an in-memory file system with the following operations:
ls(path)
: Given a path, return the list of file and directory names in the directory (not including subdirectories). The order should be lexicographical.mkdir(path)
: Create a directory at the given path. If the parent directory does not exist, create it as well.addContentToFile(filePath, content)
: Append the given content to the file. If the file does not exist, create it. If the parent directory does not exist, create it as well.readContentFromFile(filePath)
: Return the content of the file.Assume that:
/
.Example:
FileSystem fs = new FileSystem();
fs.ls("/"); // Returns []
fs.mkdir("/a/b/c");
fs.addContentToFile("/a/b/c/d", "hello");
fs.readContentFromFile("/a/b/c/d"); // Returns "hello"
fs.addContentToFile("/a/b/c/d", " world");
fs.readContentFromFile("/a/b/c/d"); // Returns "hello world"
fs.ls("/"); // Returns ["a"]
fs.ls("/a"); // Returns ["b"]
fs.ls("/a/b"); // Returns ["c"]
fs.ls("/a/b/c"); // Returns ["d"]
How would you implement this file system, keeping in mind efficiency and scalability?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
Let's create a very simple file system in our computer's memory. A brute force method would involve directly searching and creating the files and folders whenever needed, without any clever organization.
Here's how the algorithm would work step-by-step:
class FileSystem:
def __init__(self):
self.root = {}
def create_file(self, file_path, content):
path_components = file_path.split('/')
current_directory = self.root
for i in range(1, len(path_components) - 1):
component = path_components[i]
if component not in current_directory:
return False
current_directory = current_directory[component]
file_name = path_components[-1]
# Check if file exists already
if file_name in current_directory:
return False
current_directory[file_name] = content
return True
def create_directory(self, directory_path):
path_components = directory_path.split('/')
current_directory = self.root
for i in range(1, len(path_components)):
component = path_components[i]
# Directory exists, creation fails
if component in current_directory:
return False
current_directory[component] = {}
current_directory = current_directory[component]
return True
def read_file(self, file_path):
path_components = file_path.split('/')
current_directory = self.root
for i in range(1, len(path_components) - 1):
component = path_components[i]
if component not in current_directory:
return None
current_directory = current_directory[component]
file_name = path_components[-1]
if file_name not in current_directory:
return None
# Return content if file is found
return current_directory[file_name]
def list_contents(self, directory_path):
path_components = directory_path.split('/')
current_directory = self.root
for i in range(1, len(path_components)):
component = path_components[i]
if component not in current_directory:
return None
current_directory = current_directory[component]
# Returns all immediate children
return list(current_directory.keys())
We'll simulate a simplified file system in memory. The core idea is to represent the file system structure using a tree-like organization, where each node can be either a directory or a file.
Here's how the algorithm would work step-by-step:
class File:
def __init__(self, name, content):
self.name = name
self.content = content
class Directory:
def __init__(self, name):
self.name = name
self.children = {}
class FileSystem:
def __init__(self):
# Initialize the root directory for the file system.
self.root = Directory("/")
def create_directory(self, path):
path_parts = path.split("/")
current_directory = self.root
for part in path_parts[1:]:
if not part:
continue
if part not in current_directory.children:
current_directory.children[part] = Directory(part)
current_directory = current_directory.children[part]
def create_file(self, path, content):
path_parts = path.split("/")
file_name = path_parts[-1]
current_directory = self.root
for part in path_parts[1:-1]:
if not part:
continue
if part not in current_directory.children:
# Create intermediate directories if they don't exist.
current_directory.children[part] = Directory(part)
current_directory = current_directory.children[part]
current_directory.children[file_name] = File(file_name, content)
def read_file(self, path):
path_parts = path.split("/")
file_name = path_parts[-1]
current_directory = self.root
for part in path_parts[1:-1]:
if not part:
continue
current_directory = current_directory.children[part]
if file_name in current_directory.children:
return current_directory.children[file_name].content
else:
return None
def list_directory(self, path):
path_parts = path.split("/")
current_directory = self.root
for part in path_parts[1:]:
if not part:
continue
# Navigate to the target directory.
if part in current_directory.children:
current_directory = current_directory.children[part]
else:
return []
# Extract the names of all children (files and directories).
return list(current_directory.children.keys())
Case | How to Handle |
---|---|
Null or empty path | Return error or handle gracefully if the path is null or empty, depending on the operation (create, delete, read). |
Path contains consecutive slashes '//' | Normalize the path by collapsing consecutive slashes into a single slash. |
Path is too long exceeding the maximum file system path length | Return an error indicating the path is too long. |
Creating a file or directory with the same name as an existing one. | Return an error if attempting to create a file/directory with the same name as an existing one, or overwrite depending on specification. |
Deleting a non-empty directory. | Implement recursive delete if necessary, or return error if non-empty directories cannot be deleted directly. |
Reading from a file that does not exist or a directory | Return an error indicating that the file does not exist or it is a directory. |
Writing to a file exceeding maximum file size | Return an error or handle file truncation depending on specification. |
Insufficient memory to create a new file or directory. | Return an error indicating insufficient memory. |