英文:
Algorithm to index flat object list to dropdown tree?
问题
I'm having a little bit of a hard time figuring out an algorithm to index a list of strings I have, to be able to order them in a tree hierarchy way.
I have an s3 bucket with thousands of files. The file's names are their path in a windows OS, for example:
root/mainfolder/folder/file.txt
root/mainfolder/folder/file1.txt
root/mainfolder/folder2/file.txt
root/mainfolder/folder3/file1.txt
root/mainfolder2/folder4/file7.txt
root/mainfolder/file.txt
....
I want to create a dropdown tree for all the files.
I will be using this template, so I have been trying to create an object for each file with the following properties.
templateData.Value = "id";
templateData.Text = "name";
templateData.Expanded = "expanded";
templateData.HasChildren = "hasChild";
templateData.ParentValue = "pid";
My approach so far has been using the split()
function to separate each folder and file into single strings.
item = "root/mainfolder/folder/file.txt";
string[] split = item.Split('/');
split[0] = root;
split[1] = mainfolder;
split[2] = folder;
split[3] = file.txt;
Then I iterate the split list and create an object in a list called DropDownTree
for each folder.
When I get the next item in the s3 folder I would use:
(DropDownTree.FirstOrDefault(x => x.Name == split[i]) == null)
Meaning, if that folder did not exist in the DropDownTree list I am creating, I would create the object; otherwise, I would just advance to the next index in the split. But it's really not working and is super slow.
Which approaches are best in this case? Has anyone tried an algorithm like this?
Thank you.
英文:
I'm having a little bit of a hard time figuring out an algorithm to index a list of string I have, to be able to order them in a tree hierarchy way.
I have an s3 bucket with thousands of files. The file's names are their path in a windows OS, for example:
root/mainfolder/folder/file.txt
root/mainfolder/folder/file1.txt
root/mainfolder/folder2/file.txt
root/mainfolder/folder3/file1.txt
root/mainfolder2/folder4/file7.txt
root/mainfolder/file.txt
....
I want to create a dropdown tree for all the files.
I will be using this template, so I have been trying to to create an object for each file with the following properties.
templateData.Value = "id";
templateData.Text = "name";
templateData.Expanded = "expanded";
templateData.HasChildren = "hasChild";
templateData.ParentValue = "pid";
My approach so far has been using the split()
funtion to separate each folder and file in single strings.
item = "root/mainfolder/folder/file.txt";
string[] split = item.Split('/');
split [0] = root;
split [1] = mainfolder;
split [2] = folder;
split [3] = file.txt;
Then I iterate the split list, and create an object in a list called DropDownTree
for each folder.
When I get the next item in the s3 folder I would use:
(DropDownTree.FirstOrDefault(x => x.Name == split[i]) == null)
Meaning, if that folder did not existed in the DropDownTree list I am creating, I would created the object, otherwise I would just advance to the next index in the split. But is really not working and is super slow.
Which approaches are best in this case? Has anyonetried an algorithm like this?
Thank you.
答案1
得分: 2
第一步是将您的路径列表解析为一组对象:
- 文件对象,其中每个文件具有一个名称
- 目录对象,其中每个目录具有其包含的文件的集合,以及子目录的集合:
record Directory(string Name)
{
public Dictionary<string, Directory> SubDirectories { get; } = new();
public Dictionary<string, File> Files { get; } = new();
}
record File(string Name)
{
}
然后,我们将从一个虚拟的根目录对象开始,其中包含所有根目录下的文件/目录:
var root = new Directory("");
接下来,我们将遍历每个路径,并将其分割为各个部分,就像您所做的那样。然后,我们将开始遍历这些部分,并同时沿着目录层次结构向下遍历。因此,给定路径a/b/c.txt
,我们将从上面创建的root
目录开始,查找名为a
的子目录:如果找不到,我们将创建它。
我们将下移一级,并在我们刚刚找到/创建的a
目录对象上查找名为b
的子目录。最后,在b
的目录对象上创建名为c.txt
的文件。
代码示例如下:
foreach (string path in paths)
{
var parts = path.Split('/');
var currentDirectory = root;
for (int i = 0; i < parts.Length; i++)
{
if (i < parts.Length - 1)
{
// 目录名称
if (!currentDirectory.SubDirectories.TryGetValue(parts[i], out var child))
{
child = new Directory(parts[i]);
currentDirectory.SubDirectories.Add(parts[i], child);
}
currentDirectory = child;
}
else
{
// 文件名称
currentDirectory.Files.Add(parts[i], new File(parts[i]));
}
}
}
英文:
The first step is to parse your list of paths into a set of objects:
- File objects, where each File has a Name
- Directory objects, where each Directory has a collection of Files that it contains, and a collection of SubDirectories:
record Directory(string Name)
{
public Dictionary<string, Directory> SubDirectories { get; } = new();
public Dictionary<string, File> Files { get; } = new();
}
record File(string Name)
{
}
We'll then start with a dummy root Directory object, which contains all of the files/directories at the root:
var root = new Directory("");
We'll then go through each of the paths and split each into its parts, as you did. We'll then start walking through the parts, and walking down the directory hierarchy at the same time. So given the path a/b/c.txt
, we'll start at the root
directory that we created above and look for a sub-directory called a
: if we don't find one, we'll create it.
We'll move down a level and look for a sub-directory called b
on new Directory object for a
we just found/created. Finally, we'll create a file called c.txt
on the Directory object for b
.
Something like:
foreach (string path in paths)
{
var parts = path.Split('/');
var currentDirectory = root;
for (int i = 0; i < parts.Length; i++)
{
if (i < parts.Length - 1)
{
// Directory name
if (!currentDirectory.SubDirectories.TryGetValue(parts[i], out var child))
{
child = new Directory(parts[i]);
currentDirectory.SubDirectories.Add(parts[i], child);
}
currentDirectory = child;
}
else
{
// File name
currentDirectory.Files.Add(parts[i], new File(parts[i]));
}
}
}
Now we've got this, we can walk down it and create our template objects as necessary.
答案2
得分: 1
以下是您要的代码部分的中文翻译:
我认为直接对文件系统进行解析更好。
因此,考虑以下的标记:
<asp:TreeView ID="TreeView1" runat="server" ImageSet="XPFileExplorer"
NodeIndent="15" OnTreeNodeExpanded="TreeView1_TreeNodeExpanded">
<HoverNodeStyle Font-Underline="True" ForeColor="#6666AA" />
<NodeStyle Font-Names="Tahoma" Font-Size="8pt" ForeColor="Black"
HorizontalPadding="2px" NodeSpacing="0px" VerticalPadding="2px" />
<ParentNodeStyle Font-Bold="False" />
<SelectedNodeStyle BackColor="#B5B5B5" Font-Underline="False"
HorizontalPadding="0px" VerticalPadding="0px" />
</asp:TreeView>
所以,我们的代码不需要递归。
但是,我们想要一个“东西”作为文件夹,然后在文件夹中有“文件”,或者可能是文件和一些文件夹。
因此,这段代码相当不错:
string sRoot = @"c:\SERVER01";
public class MyFolder
{
public FileInfo[] MyFiles;
public DirectoryInfo[] MyFolders;
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lblFolder.Text = sRoot;
MyFolder MyFiles = GetFiles(sRoot);
LoadTreeFiles(sRoot, MyFiles, "", null);
}
}
public MyFolder GetFiles(string sRoot)
{
DirectoryInfo MyDir = new DirectoryInfo(sRoot);
MyFolder cResultFolder= new MyFolder();
cResultFolder.MyFolders = MyDir.GetDirectories("*.*", SearchOption.TopDirectoryOnly);
cResultFolder.MyFiles = MyDir.GetFiles("*.*", SearchOption.TopDirectoryOnly);
return cResultFolder;
}
public void LoadTreeFiles(string fRootL,
MyFolder lParent,
string sParentID,
TreeNode tTreeNode)
{
// 获取所有文件夹(如果有的话)
foreach (DirectoryInfo sFolder in lParent.MyFolders)
{
TreeNode child = new TreeNode();
child.Text = sFolder.Name;
child.Value = sFolder.FullName;
child.Expanded = false;
child.PopulateOnDemand = true;
child.ShowCheckBox = false;
if (sParentID == "")
TreeView1.Nodes.Add(child);
else
tTreeNode.ChildNodes.Add(child);
}
// 获取所有文件(如果有的话)
foreach (FileInfo sFile in lParent.MyFiles)
{
TreeNode childF = new TreeNode();
childF.Value = sFile.Name;
childF.Value = sFile.FullName;
childF.Expanded = false;
childF.ShowCheckBox = true;
childF.PopulateOnDemand = false;
if (sParentID == "")
TreeView1.Nodes.Add(childF);
else
tTreeNode.ChildNodes.Add(childF);
}
}
还需要一个事件 - "展开"文件夹的代码部分。
因此,这段代码:
protected void TreeView1_TreeNodeExpanded(object sender, TreeNodeEventArgs e)
{
TreeNode child = e.Node;
MyFolder dtChild = GetFiles(child.Value);
LoadTreeFiles(child.Value, dtChild, child.Text, child);
}
现在的结果如下:
英文:
I think it better to parse directly against the file system.
So, say this markup:
<asp:TreeView ID="TreeView1" runat="server" ImageSet="XPFileExplorer"
NodeIndent="15" OnTreeNodeExpanded="TreeView1_TreeNodeExpanded">
<HoverNodeStyle Font-Underline="True" ForeColor="#6666AA" />
<NodeStyle Font-Names="Tahoma" Font-Size="8pt" ForeColor="Black"
HorizontalPadding="2px" NodeSpacing="0px" VerticalPadding="2px" />
<ParentNodeStyle Font-Bold="False" />
<SelectedNodeStyle BackColor="#B5B5B5" Font-Underline="False"
HorizontalPadding="0px" VerticalPadding="0px" />
</asp:TreeView>
So, our code does NOT need to be recursive.
but, we want to have a "thing" that is a folder, and in that folder we have "files", or maybe files and some folders.
So, this code works rather nice:
string sRoot = @"c:\SERVER01";
public class MyFolder
{
public FileInfo[] MyFiles;
public DirectoryInfo[] MyFolders;
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lblFolder.Text = sRoot;
MyFolder MyFiles = GetFiles(sRoot);
LoadTreeFiles(sRoot, MyFiles, "", null);
}
}
public MyFolder GetFiles(string sRoot)
{
DirectoryInfo MyDir = new DirectoryInfo(sRoot);
MyFolder cResultFolder= new MyFolder();
cResultFolder.MyFolders = MyDir.GetDirectories("*.*", SearchOption.TopDirectoryOnly);
cResultFolder.MyFiles = MyDir.GetFiles("*.*", SearchOption.TopDirectoryOnly);
return cResultFolder;
}
public void LoadTreeFiles(string fRootL,
MyFolder lParent,
string sParentID,
TreeNode tTreeNode)
{
// get all folders (if any)
foreach (DirectoryInfo sFolder in lParent.MyFolders)
{
TreeNode child = new TreeNode();
child.Text = sFolder.Name;
child.Value = sFolder.FullName;
child.Expanded = false;
child.PopulateOnDemand = true;
child.ShowCheckBox = false;
if (sParentID == "")
TreeView1.Nodes.Add(child);
else
tTreeNode.ChildNodes.Add(child);
}
// get all files(if any)
foreach (FileInfo sFile in lParent.MyFiles)
{
TreeNode childF = new TreeNode();
childF.Value = sFile.Name;
childF.Value = sFile.FullName;
childF.Expanded = false;
childF.ShowCheckBox = true;
childF.PopulateOnDemand = false;
if (sParentID == "")
TreeView1.Nodes.Add(childF);
else
tTreeNode.ChildNodes.Add(childF);
}
}
And we need one more event - the "expand" a folder bit of code.
So this:
protected void TreeView1_TreeNodeExpanded(object sender, TreeNodeEventArgs e)
{
TreeNode child = e.Node;
MyFolder dtChild = GetFiles(child.Value);
LoadTreeFiles(child.Value, dtChild, child.Text, child);
}
And the result is now this:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论