-3

I have a list with many files. I want to show this list in a tree structure instead of a flat list. Something like this:

2001
- 2001-01
    -2001-01-01
        -File 1
        -File 2
        -File 3
        -File 4
    -2001-01-02
        -File 5
        -File 6
        -File 7
        -File 8
    -2001-01-03
        - etc
    -2001-01-03
etc...

I found something similar but dont know how to apply it with my settings. How to efficiently build a tree from a flat structure?

The files have a DateTime field where I get the date.

Can I group all my files with the same date?

edit: Right now the list is displayed like this

2001-01-01 - File 1
2001-01-01 - File 2
2001-01-01 - File 3
2001-01-01 - File 4
2001-01-02 - File 5
2001-01-02 - File 6
2001-01-02 - File 7
etc
Community
  • 1
  • 1
  • I'm not quite sure where exactly your problem lies. Maybe you're looking for the [TreeView class](http://msdn.microsoft.com/de-de/library/system.windows.controls.treeview%28v=vs.110%29.aspx) – Vogel612 Jan 12 '15 at 14:46
  • My problem is that I got a flat list. If i loop through it i get this: 2001-01-01 - File 1 2001-01-01 - File 2 2001-01-01 - File 3 2001-01-01 - File 4 2001-01-02 - File 5 etc I want to have the structure in my question. – lindesvard Jan 12 '15 at 15:06
  • 3
    Right. You'll need to sort them into lists, and nested lists.. maybe even three levels deep. Your first level is by year. Within that list, maintain a list of year+month. Within that list, maintain a list of year+month+day. Once you're done expanding the file list into this hierarchy, walk through each list recursively, and build a tree with nodes. – Lynn Crumbling Jan 12 '15 at 15:10
  • Do you want this to create a tree view object of some kind, or do you just want to output text or HTML? – Jim Mischel Jan 14 '15 at 05:29

3 Answers3

2

I assume you have a sorted array named Files of FileInfo-objects. Then you can iterate through it like:

foreach (FileInfo fi in Files)
{
    AddNode(fi);
}

The AddNode member adds the nodes to the tree:

private void AddNode(FileInfo F)
{
    // Create the node labels.
    string y = String.Format("{0:yyyy}", F.LastWriteTime);
    string ym = String.Format("{0:yyyy-MM}", F.LastWriteTime);
    string ymd = String.Format("{0:yyyy-MM-dd}", F.LastWriteTime);

    TreeNode[] tnY = null;
    TreeNode[] tnYM = null;
    TreeNode[] tnYMD = null;

    // Find existing tree nodes for the file we are working on.
    tnY = treeView1.Nodes.Find(y, false);
    if (tnY.Length == 1)
    {
        tnYM = tnY[0].Nodes.Find(ym, false);
        if (tnYM.Length == 1)
        {
            tnYMD = tnYM[0].Nodes.Find(ymd, false);
        }
    }

    // Create the missing nodes.
    if (tnY.Length == 0)
    {
        tnY = new TreeNode[1];
        tnY[0] = treeView1.Nodes.Add(y, y);
    }
    if (tnYM == null || tnYM.Length == 0)
    {
        tnYM = new TreeNode[1];
        tnYM[0] = tnY[0].Nodes.Add(ym, ym);
    }
    if (tnYMD == null || tnYMD.Length == 0)
    {
        tnYMD = new TreeNode[1];
        tnYMD[0] = tnYM[0].Nodes.Add(ymd, ymd);
    }

    // And finally, add the node with the file name.
    tnYMD[0].Nodes.Add(F.Name);
}
R. Beiboer
  • 712
  • 9
  • 21
1

Okay, it is just a very straightforward implementation, but it does the trick :)

List<string> files = new List<string>()
{
    {"2001-01-01 - File 1"},
    {"2001-01-01 - File 2"},
    {"2001-01-02 - File 1"},
    {"2001-01-03 - File 1"}
};

TreeView tv = new TreeView();

foreach(string file in files)
{
    string y = file.Substring(0, 4);
    string ym = file.Substring(0, 7);
    string ymd = file.Substring(0, 10);
    string fn = file.Substring(13);

    TreeNode Node = tv.Nodes[y] ?? tv.Nodes.Add(y,y);
    Node = Node.Nodes[ym] ?? Node.Nodes.Add(ym,ym);
    Node = Node.Nodes[ymd] ?? Node.Nodes.Add(ymd,ymd);
    Node.Nodes.Add(fn);
}

EDIT: So, I didn't really notice the information used a DateTime. In order for this, I now use a List filled with classes having a DateTime and a string filename:

List<fi> files = new List<fi>()
{
    new fi(new DateTime(2001, 1, 1), "File 1"),
    new fi(new DateTime(2001, 1, 1), "File 2"),
    new fi(new DateTime(2001, 1, 3), "File 3"),
    new fi(new DateTime(2001, 1, 1), "File 4"),
    new fi(new DateTime(2001, 1, 2), "File 5"),
    new fi(new DateTime(2001, 1, 2), "File 6"),
    new fi(new DateTime(2001, 1, 2), "File 7")
};

TreeView tv = new TreeView();

var orderedfiles = from file in files orderby file.date ascending select file;
foreach(fi file in orderedfiles)
{
    TreeNode Node = tv.Nodes[file.date.Year.ToString()] ?? tv.Nodes.Add(file.date.Year.ToString(), file.date.Year.ToString());
    Node = Node.Nodes[file.date.ToString("yyyy-mm")] ?? Node.Nodes.Add(file.date.ToString("yyyy-mm"), file.date.ToString("yyyy-mm"));
    Node = Node.Nodes[file.date.ToString("yyyy-mm-dd")] ?? Node.Nodes.Add(file.date.ToString("yyyy-mm-dd"), file.date.ToString("yyyy-mm-dd"));
    Node.Nodes.Add(file.fn);
}
RFerwerda
  • 1,267
  • 2
  • 10
  • 24
1

The basic idea is to keep track of the previous year, month, and day so that you know if you should create new parent nodes. You don't say that you want this displayed in a tree view, so I'm going to assume you want it in standard text or HTML. Making it a tree view control is a little more complicated in implementation, but the fundamental idea isn't any different.

Rather than get bogged down in the specifics of a C# implementation, I'll present the pseudocode. That will make it easier for me to get the ideas across, and easier for you to graft into whatever form you need to fit your data and output method.

This assumes that the files are sorted by date, with the earliest date (i.e. oldest file) first.

Start by setting the previous day, month, and year to -1:

previousYear = -1
previousMonth = -1
previousDay = -1
for each file in list
{
    if (file.date.year != previousYear)
    {
        output file.date.year
        previousYear = file.date.year
        // set previousMonth to default to force a new month folder
        previousMonth = -1
    }
    if (file.date.Month != previousMonth)
    {
        output file.date.month
        previousMonth = file.date.month
        // set previousDay to default to force a new day folder
        previousDay = -1
    }
    if (file.date.day != previousDay)
    {
        output file.date.day
        previousDay = file.date.day
    }
    // and then output the file name
    output file.name
}
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351