Dicekey
Dicekey

Reputation: 405

C# Sort Parent/Child list to produce a flat output

I have this model:

public class Node 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? ParentId { get; set; }
}

I have The following data that comes from a database query:

        nodes.Add(new Node { Id = 1, Name = "Node #1", ParentId = null });
        nodes.Add(new Node { Id = 2, Name = "Node #2", ParentId = 1 });
        nodes.Add(new Node { Id = 3, Name = "Node #3", ParentId = 2 });
        nodes.Add(new Node { Id = 4, Name = "Node #4", ParentId = null });
        nodes.Add(new Node { Id = 5, Name = "Node #5", ParentId = 2 });
        nodes.Add(new Node { Id = 6, Name = "Node #6", ParentId = 2 });
        nodes.Add(new Node { Id = 7, Name = "Node #7", ParentId = 1 });
        nodes.Add(new Node { Id = 8, Name = "Node #8", ParentId = 5 });
        nodes.Add(new Node { Id = 9, Name = "Node #9", ParentId = 4 });
        nodes.Add(new Node { Id = 10, Name = "Node #10", ParentId = 4 });

I would like to sort the list and maintain the flat structure. The output I am expecting is this:

        // 1  - Node #1  => NULL
        // 2  - Node #2  => 1
        // 3  - Node #3  => 2
        // 5  - Node #5  => 2
        // 8  - Node #8  => 5
        // 6  - Node #6  => 2
        // 7  - Node #7  => 1
        // 4  - Node #4  => NULL
        // 9  - Node #9  => 4
        // 10 - Node #10 => 4

I was referring to this Stackoverflow answer but I didn't get the result I want.

Any help?

Upvotes: 2

Views: 598

Answers (3)

MKR
MKR

Reputation: 20085

You can try creating recursive query in LINQ using SelectMany as:

IEnumerable<Node> Recurcive(List<Node> nodeList, int? parentId)
{
    return nodeList
        .Where(x => x.ParentId == parentId)
        .SelectMany(x =>
                  new[] { new Node
                    { Id = x.Id, Name = x.Name, ParentId = x.ParentId } }
                        .Concat(Recurcive(nodeList, x.Id)));
}

foreach (var node in Recurcive(nodes, null))
    Console
     .WriteLine($"Id : {node.Id}\t, Name = {node.Name}\t, Parent = {node.ParentId}");

Output:

//Id: 1  , Name = Node #1        , Parent =
//Id: 2  , Name = Node #2        , Parent = 1
//Id: 3  , Name = Node #3        , Parent = 2
//Id: 5  , Name = Node #5        , Parent = 2
//Id: 8  , Name = Node #8        , Parent = 5
//Id: 6  , Name = Node #6        , Parent = 2
//Id: 7  , Name = Node #7        , Parent = 1
//Id: 4  , Name = Node #4        , Parent =
//Id: 9  , Name = Node #9        , Parent = 4
//Id: 10 , Name = Node #10       , Parent = 4

Upvotes: 0

Enigmativity
Enigmativity

Reputation: 117057

Here's how I would do it:

var nodes = new List<Node>()
{
    new Node { Id = 1, Name = "Node #1", ParentId = null },
    new Node { Id = 2, Name = "Node #2", ParentId = 1 },
    new Node { Id = 3, Name = "Node #3", ParentId = 2 },
    new Node { Id = 4, Name = "Node #4", ParentId = null },
    new Node { Id = 5, Name = "Node #5", ParentId = 2 },
    new Node { Id = 6, Name = "Node #6", ParentId = 2 },
    new Node { Id = 7, Name = "Node #7", ParentId = 1 },
    new Node { Id = 8, Name = "Node #8", ParentId = 5 },
    new Node { Id = 9, Name = "Node #9", ParentId = 4 },
    new Node { Id = 10, Name = "Node #10", ParentId = 4 },
};

var lookup = nodes.ToLookup(x => x.ParentId);

IEnumerable<Node> Flatten(int? parentId)
{
    foreach (var node in lookup[parentId])
    {
        yield return node;
        foreach (var child in Flatten(node.Id))
        {
            yield return child;
        }
    }   
}

var output = Flatten(null).ToArray();

That little bit of recursion gives me:

Flattened List

Upvotes: 3

Hogan
Hogan

Reputation: 70523

I think you want this

 nodes.OrderBy(n => n.ParentID ?? n.Id)
      .ThenBy(n => n.Id);

Upvotes: 0

Related Questions