1

My situation is that I am given a List which represents a directory structure, in the following format:

"My Folder\Images" "My Folder\Images\Gif" "My Folder\Images\JPG" "My Folder\Media" "My Folder\Media\Mov" "My Folder\Media\Mov\QT" "My Folder\Media\MPG" 

There is no restriction on how many levels this can be nested.

I need to build something which represents a treeview from this, in the format:

public class Folder { public string FolderName { get; set; } public List<Folder> Folders{ get; set; } // a list of subfolders } 

I just can't get the recrusive function which builds this quite right. Any help from the Gurus would be greatly appreciated.

TIA

Edit: My full class definition is:

public class Folder { public string Name { get; set; } public List<Folder> Folders { get; set; } public Folder(List<string> input) { foreach (var folder in input) { var delimPos = folder.IndexOf("\\"); if (delimPos == -1) { Name = folder ; } else { Name = folder.Substring(0, delimPos); var subFolders= input.Select(o => o.Substring(delimPos + 1)).ToList(); Folders= new List<Folder>(); foreach (var subFolder in subFolders) { Folders.Add(new Folder(new List<string>() { subFolder })); } } } } } 
5
  • 4
    Share your recursive function. Lets see what is wrong with that Commented Jun 6, 2018 at 9:57
  • Edit: Full class definition added. @PonSaravanan Commented Jun 6, 2018 at 10:17
  • I am a bit confused now. Do you want to traverse a single string or a list of strings? Commented Jun 6, 2018 at 10:26
  • I wish to have each folder a name property and also a list of the subfolders it contains (and each subfolder to do the same). Commented Jun 6, 2018 at 10:33
  • Check out the non-recursive approach below and let me know if it works out for you. Commented Jun 6, 2018 at 13:34

3 Answers 3

2

You of course have the problem that after splitting and processing Images\Gif, you're going to re-add another Images folder when splitting and processing Images\JPG. The first will have a Gif subfolder, the second will have a JPG subfolder.

You can fix this by grouping on the first part, and only processing the parts that follow:

public static List<Folder> ParseInputRecursive(string[] input) { var foldersInParts = input.Select(f => f.Split(new [] { '\\' }, StringSplitOptions.RemoveEmptyEntries).ToList()).ToList(); return ParseInputRecursive(foldersInParts); } public static List<Folder> ParseInputRecursive(List<List<string>> input) { var folders = new List<Folder>(); foreach (var folderPartsGroup in input.GroupBy(p => p[0])) { var folder = new Folder { Name = folderPartsGroup.Key }; // Remove parent name, skip parent itself var subFolders = folderPartsGroup.Select(f => f.Skip(1).ToList()).Where(f => f.Count > 0).ToList(); folder.Folders = ParseInputRecursive(subFolders); folders.Add(folder); } return folders; } 

Printing them to verify:

// Sort to make sure parents always come first Array.Sort(input); var rootFolders = ParseInputRecursive(input); foreach (var folder in rootFolders) { PrintFoldersRecursive(folder); } public static void PrintFoldersRecursive(Folder folder, int depth = 0) { Console.WriteLine(new string('*', depth++) + folder.Name); foreach (var subFolder in folder.Folders) { PrintFoldersRecursive(subFolder, depth); } } 

Given this input:

var input = new string[] { @"F1\Images", @"F1\Images\Gif", @"F1\Images\JPG", @"F1\Media", @"F1\Media\Mov", @"F2\Docs", @"F2\Docs\Foo", }; 

Gives this output:

F1 *Images **Gif **JPG *Media **Mov F2 *Docs **Foo 
Sign up to request clarification or add additional context in comments.

1 Comment

Perfect. Exactly what I needed.
0

For a simple node traversal by string parsing please refer the below example

private void button1_Click(object sender, EventArgs e) { var folders = ProcessNode("My Folder\\Media\\Mov\\QT", 0); MessageBox.Show("finished"); } private Folder ProcessNode(string input, int index) { var newIndex = input.IndexOf("\\", index + 1); if (newIndex < 0) return new Folder() { FolderName = input.Substring(index, input.Length - index) }; var nodeName = input.Substring(index, newIndex - index); var thisFolder = new Folder() { FolderName = nodeName, Folders = new List<Folder>() }; thisFolder.Folders.Add(ProcessNode(input, newIndex)); return thisFolder; } 

If you want to use the split function and list

 private void button1_Click(object sender, EventArgs e) { var splitted = "My Folder\\Media\\Mov\\QT".Split('\\').ToList(); var foldersList = ProcessNode(splitted, 0); MessageBox.Show("finished"); } private Folder ProcessNode(List<string> input, int index) { if (index >= input.Count) return null; var thisFolder = new Folder() { FolderName = input[index], Folders = new List<Folder>() }; thisFolder.Folders.Add(ProcessNode(input, index + 1)); return thisFolder; } 

Comments

0

Below is a non-recursive solution, it parses and prints the values as desired. Let me know if it helps. Demo

Logic

 public static IEnumerable<Folder> Parse(IEnumerable<string> locations) { var folders = new List<Folder>(); foreach (var location in locations) { var parts = location.Split(new[]{Path.DirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries); Folder currentFolder = null; foreach (var part in parts) { var parentFolders = currentFolder!=null ? currentFolder.Folders : folders; currentFolder = parentFolders.Find(folder => folder.Name == part) ?? new Folder { Name = part }; if (!parentFolders.Any(folder => folder.Name.Equals(currentFolder.Name))) { parentFolders.Add(currentFolder); } } } return folders; } 

Full Program

//Rextester.Program.Main is the entry point for your code. Don't change it. //Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5 using System; using System.IO; using System.Linq; using System.Collections.Generic; namespace Rextester { public class Folder { public string Name { get; set; } public List<Folder> Folders { get; internal set; } public Folder() { this.Folders = new List<Folder>(); } public static IEnumerable<Folder> Parse(IEnumerable<string> locations) { var folders = new List<Folder>(); foreach (var location in locations) { var parts = location.Split(new[]{Path.DirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries); Folder currentFolder = null; foreach (var part in parts) { var parentFolders = currentFolder!=null ? currentFolder.Folders : folders; currentFolder = parentFolders.Find(folder => folder.Name == part) ?? new Folder { Name = part }; if (!parentFolders.Any(folder => folder.Name.Equals(currentFolder.Name))) { parentFolders.Add(currentFolder); } } } return folders; } public override string ToString() { if (this.Folders.Count == 0) { return this.Name; } else { var folders = this.Folders .SelectMany(folder => folder.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None)) .Select(f => this.Name + Path.DirectorySeparatorChar + f); return string.Join(Environment.NewLine, folders); } } } public class Program { public static void Main(string[] args) { var locationList = new List<string>() { @"My Folder\Images", @"My Folder\Media", @"My Folder\Images\Gif", @"My Folder\Images\JPG", @"My Folder\Media\Mov", @"My Folder\Media\Mov\QT", @"My Folder\Media\MPG" }; var folderLists = Folder.Parse(locationList); Console.WriteLine(string.Join(Environment.NewLine, folderLists)); Console.ReadLine(); } } } 

Output:

My Folder\Images\Gif My Folder\Images\JPG My Folder\Media\Mov\QT My Folder\Media\MPG 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.