2

First, I did check this post but it is in Python, first, and second it appears to be actually making the directories, which I cannot do in this scenario.

Second, these are not directories that exist, nor can I create them.

I have an input in C# like this:

        List<string> filePaths = new List<string>();
        filePaths.Add(@"ProgramDir\InstallDir\Module1\mod1pack1.exe");
        filePaths.Add(@"ProgramDir\InstallDir\Module1\mod1pack2.exe");
        filePaths.Add(@"ProgramDir\InstallDir\Module2\mod2pack1.exe");
        filePaths.Add(@"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt");
        filePaths.Add(@"SystemDir\DependencyDir\dependency1.dll");
        filePaths.Add(@"SystemDir\DependencyDir\dependency2.dll");

What I have been trying to do is create an object that represents this structure, such that it could be visualized like this:

-ProgramDir
    Installdir
        Module1
            mod1pack1.exe
            mod1pack2.exe
            -SubModule1
                 report1.rpt
        Module2
            mod2pack1.exe
-SystemDir
    -DependencyDir
         dependency1.dll
     dependency2.dll

What I have tried is various versions of the following, and I could really use some help to figure out where I've got it wrong.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            SetFilePathList();
            DTree forest = new DTree();
            List<DTreeBranch> branches = new List<DTreeBranch>();
            foreach (string path in filePaths)
            {
                forest.GrowTree(path.Split('\\'), branches);
            }
            forest.SubBranches.AddRange(branches);
        }
        private static List<string> filePaths { get; set; }
        private static void SetFilePathList()
        {
            filePaths = new List<string>();
            filePaths.Add(@"ProgramDir\InstallDir\Module1\mod1pack1.exe");
            filePaths.Add(@"ProgramDir\InstallDir\Module1\mod1pack2.exe");
            filePaths.Add(@"ProgramDir\InstallDir\Module2\mod2pack1.exe");
            filePaths.Add(@"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt");
            filePaths.Add(@"SystemDir\DependencyDir\dependency1.dll");
            filePaths.Add(@"SystemDir\DependencyDir\dependency2.dll");
        }
    }
    public class DTree
    {
        public List<DTreeBranch> SubBranches { get; set; }
        public string BranchName { get; set; }
        public DTree() { SubBranches = new List<DTreeBranch>(); }
        public DTreeBranch AddChildren(string[] childElements, DTreeBranch branch)
        {
            DTreeBranch childBranch;
            foreach (string element in childElements)
            {
                childBranch = new DTreeBranch();
                childBranch.BranchName = element;
                branch.SubBranches.Add(childBranch);
                var query = from q in childElements
                            where q != childBranch.BranchName
                            select q;
                AddChildren(query.ToArray<string>(), childBranch);
            }
            return branch;
        }
        public void GrowTree(string[] pathElements, List<DTreeBranch> Branches)
        {
            DTreeBranch result = Branches.Find(delegate(DTreeBranch b)
            {
                return b.BranchName == pathElements[0];
            });

            if (result == null)
            {
                DTreeBranch newRootBranch = new DTreeBranch();
                newRootBranch.BranchName = pathElements[0];
                Branches.Add(newRootBranch);
                GrowTree(pathElements, Branches);
            }
            else
            {
                var query = from q in pathElements
                            where q != result.BranchName
                            select q;
                DTreeBranch childBranch = AddChildren(query.ToArray<string>(), result);
                Branches.Add(childBranch);
            }
        }
    }
    public class DTreeBranch
    {
        public List<DTreeBranch> SubBranches { get; set; }
        public string BranchName { get; set; }
        public DTreeBranch()
        {
            SubBranches = new List<DTreeBranch>();
        }
    }
}

The main thing is that the output is only two layers deep. I guess what I'm saying is that the new elements are added to the depth, not the breadth, and I'm at a loss as to how to effectively work through this. I also think that I have way more code than I need.

Thanks in advance.

Community
  • 1
  • 1
geekzster
  • 559
  • 8
  • 21
  • 1
    Is this homework? If so it should be tagged as such. – Chris Shain Feb 21 '12 at 03:08
  • 1
    It is absolutely not homework. This is for my own use, because I'm programmatically creating a .wxs file for WiX by consuming a file that, among other things, lists these paths in the format shown. – geekzster Feb 21 '12 at 03:15
  • 1
    Does it really matter though... This is just a fun little thing to write up, and he put up a hell of a lot of code for it too :) I would suggest giving yourself an actual name, and something of a bio, if you want to be taken seriously though. – Andrew Feb 21 '12 at 04:06

2 Answers2

1

I'm not sure exactly what our goals are, but a simple recursive parse will do it quite easily. Wrote this up, and hope it helps. You can make it significantly more fancy if you want, with DTrees and sub branches, or separate collections for Files and Directories, etc. I don't really understand what all that code in there is for. If it has something to do with WIX, I'm sorry ;) And you could always use something like this to parse it out into the tree, and then convert that sanely to a different format.

  • this assumes no duplicate leaf nodes (file names).
  • if that isn't the case, just add a sanity check like for directories.

The main "Node" class -

public class Node
{
    public string Name { get; set; }
    public bool IsDirectory { get; set; }
    public List<Node> Children = new List<Node>();

    internal void AddChildren(string f)
    {
        var dirs = Path.GetDirectoryName(f);
        if (string.IsNullOrEmpty(dirs))
        {
            // we are adding a file
            var file = Path.GetFileName(f);
            Children.Add(new Node {Name = file, IsDirectory = false});
        }
        else
        {
            // we are adding a directory
            var firstDir = dirs.Split(Path.DirectorySeparatorChar)[0];
            var childNode = Children.FirstOrDefault(d => d.Name == firstDir);
            if (childNode == null)
            {
                childNode = new Node {Name = firstDir, IsDirectory = true};
                Children.Add(childNode);
            }

            var subPath = f.Substring(firstDir.Length + 1);
            childNode.AddChildren(subPath);
        }
    }
}

Calling it is simple, like this:

var filePaths = new List<string> { 
    @"ProgramDir\InstallDir\Module1\mod1pack1.exe",
    @"ProgramDir\InstallDir\Module1\mod1pack2.exe",
    @"ProgramDir\InstallDir\Module2\mod2pack1.exe",
    @"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt",
    @"SystemDir\DependencyDir\dependency1.dll",
    @"SystemDir\DependencyDir\dependency2.dll",
};

var node = new Node { Name = "Root", IsDirectory = true };
foreach (var f in filePaths )
{
    node.AddChildren(f);
}

Printing it out (with indent per level, gives me this)

public static void PrintNode(Node node, int indent)
{
    if (indent > 0) // don't print out root directory (level 1). 
    {
        var ending = node.IsDirectory ? Path.DirectorySeparatorChar.ToString() : "*";
        Console.WriteLine("{0}{1}{2}", new string('\t', indent - 1), node.Name, ending);
    }
    node.Children.ForEach(n => PrintNode(n, indent + 1));
}

ProgramDir\
    InstallDir\
            Module1\
                    mod1pack1.exe*
                    mod1pack2.exe*
                    SubModule1\
                            report1.rpt*
            Module2\
                    mod2pack1.exe*
SystemDir\
    DependencyDir\
            dependency1.dll*
            dependency2.dll*
Andrew
  • 8,322
  • 2
  • 47
  • 70
  • I made some concessions for brevity, like a public property. In real life, dont do this! Some extra validation would be nice, too. – Andrew Feb 21 '12 at 03:58
  • 1
    Andrew this is exactly what I am looking for, except I don't need to print it out, the object is what matters. I knew I was going about it wrong, but I posted my code anyway so you could see What I Have Tried. Thanks for the time and effort, I can see where I was off course now and I've learned from your efforts. Thank you! :)) – geekzster Feb 21 '12 at 14:33
  • No problem. Printing it out is easy, and it helps with debugging. Digging around in watch window to verify this stuff is no fun. Also, I don't know if you need a 'parent' reference... if so, just change it to have `new node { Parent = this, Name = ... }` and add a Parent{} property. – Andrew Feb 22 '12 at 03:52
1

I got about the same as Andrew:

using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        public static void Main(String[] args)
        {
            var filePaths = new List<string> {@"ProgramDir\InstallDir\Module1\mod1pack1.exe", @"ProgramDir\InstallDir\Module1\mod1pack2.exe", @"ProgramDir\InstallDir\Module2\mod2pack1.exe", @"ProgramDir\InstallDir\Module1\SubModule1\report1.rpt", @"SystemDir\DependencyDir\dependency1.dll", @"SystemDir\DependencyDir\dependency2.dll"};
            var nodes = Parse(filePaths.ToArray());
            foreach (var node in nodes)
                Console.Out.WriteLine(node.ToString());
            Console.ReadLine();
        }

        public static IEnumerable<Node> Parse(params String[] paths)
        {
            var roots = new NodeSet();
            foreach (var path in paths)
            {
                var pathSplit = path.Split('\\');
                Node current = null;
                foreach (var pathElement in pathSplit)
                {
                    var currentRoots = (current == null) ? roots : current.Children;
                    if (currentRoots.Contains(pathElement))
                        current = currentRoots[pathElement];
                    else
                        currentRoots.Add(current = new Node(pathElement));
                }
            }
            return roots;
        }

        public class Node
        {
            public String Name { get; private set; }
            public NodeSet Children { get; private set; }

            public Node(String name)
            {
                if (String.IsNullOrWhiteSpace(name)) throw new ArgumentNullException("name");
                Name = name;
                Children = new NodeSet();
            }

            public override string ToString() { return ToString(1); }

            private String ToString(Int32 indent)
            {
                var indentStr = Environment.NewLine + new string('\t', indent);
                return Name + (Children.Count == 0 ? "" : indentStr + String.Join(indentStr, Children.Select(c => c.ToString(indent + 1)).ToArray()));
            }
        }

        public class NodeSet : KeyedCollection<String, Node> {
            protected override string GetKeyForItem(Node item) { return item.Name; }
        }
    }
}
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • Thanks Chris, this was helpful. I appreciate the time. One thing, the node value is not going to work as it contains the whole string basically, I need the first node to be just ProgramDir, and then the next to be InstallDir, and so on. I don't need to print this out, the object is what matters. Again, very helpful and I sincerely appreciate your time. Thank you. :)) – geekzster Feb 21 '12 at 14:29
  • Not sure what you mean- my code builds a tree of node objects, where the root objects are the unique set of root directories, each containing the unique set of subdirectories for that root, and so on. I think that's what you are after. – Chris Shain Feb 21 '12 at 15:17