Feature #6: File Management System
Implement the "File Management System" feature for our "Operating System" project.
We'll cover the following...
Description
In this feature, we will make a file management system that will maintain a file system index. We want to allow the user to conduct searches with an optional wildcard character, .
, which matches any single character. For example, .
in the string .ata
can have 26 possible search results like aata
, bata
, cata
, and so on. We must keep in mind while searching that if the searched word matches some part of an already added file or directory then it should return false
.
For this feature, the target is to have a data structure that will:
- Add a file or directory name in the data structure. A file or directory name can be added only once. It must not allow two files with the same name. Keep in mind that file and directory names are case-sensitive.
- Perform wildcard searches. The search should return
false
if the word is not found in the file system. Otherwise, the search should returntrue
. - Return a list of all the files and directories in the data structure.
Let’s take a look at how we can add, search, and get all the files and directory names with the help of the following example:
Solution
For this solution, we will make a trie, which is a hashmap. A trie is a useful data structure that is used to match exactly and conduct wildcard matches for words. Each node in it is a data structure, TrieNode
, which has the following variables:
nodes
: This is a hashmap that will be used for storing the letters of the English alphabet.complete
: This variable takes a boolean value. The value will betrue
if the node represents the last character of the file or directory name. Otherwise, it will befalse
.
While adding a file or directory we will verify, at each step, if the child node that needs to be added is already present. If it is already present, then we will just go down one step. If it is not already present, then we will add the child node into the trie and go down one step. A file or directory name must only be added once.
For searches, there are two possibilities:
- In the absence of the
.
character, the search will be as simple as the add feature. Each key in the trie is represented as a path from the root node. In this type of search, we start from the root and go down the trie, checking character by character. - When the current character is a
.
, we will match against all the children of the currentTrieNode
.
Here is how both the features will look:
Let’s take a look at the code for the solution:
defmodule FileSystem dodefstruct root: TrieNode.init, can_find: falsedef init, do: %__MODULE__{}def get_all(obj) donames = Arrays.new([])if obj.root == nil, do: [], else: dfs(obj.root, "", names) |> Arrays.to_listenddef dfs(nil, _word, names), do: namesdef dfs(node, word, names) donames = if node.complete == true, do: Arrays.append(names, word), else: names?a..?z|> Enum.to_list|> Enum.reduce(names, fn(j, names) ->n = word <> <<j>>dfs(node.nodes[<<j>>], n, names)end)enddefp node_creater_getter(curr_node, key) docurr_node.nodes|> Map.put_new(key, TrieNode.init)|> Map.get(key)enddef add(curr_node, [], _i, _n), do: %TrieNode{curr_node | complete: true}def add(curr_node, [head | tail], i, n) dochild = node_creater_getter(curr_node, head)child = add(child, tail, i+1, n)%TrieNode{curr_node | nodes: Map.put(curr_node.nodes, head, child)}enddef add(obj, word) docurr_node = obj.rootn = byte_size(word)word_list = word |> String.graphemes%__MODULE__{obj | root: add(curr_node, word_list, 0, n)}enddefp depth_first_search([], node), do: node.completedefp depth_first_search([first | tail], node) docond doString.equivalent?(first, ".") ->node.nodes|> Enum.any?(fn {_, v} -> depth_first_search(tail, v) end)true ->if !Map.has_key?(node.nodes, first), do: false, else: depth_first_search(tail, node.nodes[first])endenddef search(obj, word) donode = obj.rootword |> String.graphemes |> depth_first_search(node)endend# FileSystem()IO.puts "-----------------------------"IO.puts "PROGRAM OUTPUT:\n"IO.puts "Initializing object"obj = FileSystem.initIO.puts "add_wording \'dir\' as a directory"obj = obj |> FileSystem.add("dir")IO.puts "add_wording \'dir\' as a directory again"obj = obj |> FileSystem.add("dir")IO.puts "add_wording \'dirr\' as a directory"obj = obj |> FileSystem.add("dirr")IO.puts "add_wording \'file\' as a directory"obj = obj |> FileSystem.add("file")# IO.inspect objIO.puts "Searching if \'.ile\' exists"IO.inspect obj |> FileSystem.search(".ile")IO.puts "Searching if \'..ile\' exists"IO.inspect obj |> FileSystem.search("..ile")IO.puts "Getting all of the files\\directories:"IO.inspect obj |> FileSystem.get_all()
Complexity measures
Time complexity | Space complexity | |
---|---|---|
add |