shenku
shenku

Reputation: 12420

How do I sort a list and its children and their children using linq?

I have a parent child relationship, something like:

public class MyType {

    public IList<MyType> Children {get;set;}
    public int Order {get;set;}

}

I would like to select the list so that each level is in order.

I can do this easily for the top level:

mylist.Children.OrderBy(x => x.Order)

But how do I do it for every set of Children?

The end result would be the list or type with all its children, and there children (and so on) all sorted correctly by Order.

Thanks.

Upvotes: 5

Views: 8660

Answers (5)

Jeff Mercado
Jeff Mercado

Reputation: 134811

You can keep the children sorted if you use a SortedList as your underlying children collection. Then you can expose the Values property to get the values. Just key the items by their order when you add to the list.

e.g.,

public class MyType
{
    public MyType(int order)
    {
        this.order = order;
    }

    private int order;
    private SortedList<int, MyType> children = new SortedList<int, MyType>();

    public int Order { get { return order; } }
    public IList<MyType> Children { get { return children.Values; } }

    public void AddChild(MyType child)
    {
        children.Add(child.order, child);
    }
}

Otherwise, you'd probably want to sort the lists recursively. Using LINQ wouldn't be appropriate here. At best, LINQ would allow you to iterate over the children in a sorted order but it doesn't actually sort the underlying list unless you replace the list instance with the sorted version. If the underlying list has a Sort() method (which the generic List<T> has), then use that.

private List<MyType> children;
public void EnsureSorted()
{
    children.Sort();
    foreach (var child in children)
        child.EnsureSorted();
}

Starting off with a sorted list would be a lot easier however.

Upvotes: 2

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112279

If the end result simply has to be a list of children (instead of a list of parents with nested children), you can use a SelectMany

IEnumerable<Child> result  = parents
    .SelectMany(p => p.Children)
    .OrderBy(child => child.Order);

Upvotes: 0

Chris Pitman
Chris Pitman

Reputation: 13104

I agree with Jeff that the easiest answer is to store the data sorted if that is going to be your main access pattern. But lets say you really want to do this with Linq:

First off, if you knew you only wanted two levels of ordering, you could do something like this:

myList.Children.OrderBy(x => x.Order)
    .Select(c => c.Children.OrderBy(x => x.Order))

But what if what you really want is totally recursive ordering, all the way down?

delegate IEnumerable<MyType> RecursiveFunc(MyType data, RecursiveFunc self);
RecursiveFunc op = (data, func) => data.Children.OrderBy(x => x.Order)
    .Select(x => func(x, func));

IEnumerable<MyType> result = op(myList, op);

Just writing that makes my brain hurt, and I haven't tried running it, so best of luck! What it comes down to is passing a linq expression(lambda) to itself, to recursively apply itself down the tree.

Upvotes: 1

cuongle
cuongle

Reputation: 75306

You can do Recursive Order by adding one method to MyType like this:

public class MyType
{
    public IList<MyType> Childrens { get; set; }
    public int Order { get; set; }

    public void RecursiveOrder()
    {
        Childrens = Childrens.OrderBy(x => x.Order)
            .ToList();

        Childrens.ToList().ForEach(c => c.RecursiveOrder());
    }
} 

Upvotes: 6

Anant Dabhi
Anant Dabhi

Reputation: 11104

thry this

mylist.Children.OrderBy(x => x.Order).ThenBy( x => x.order).ToList();

Upvotes: 0

Related Questions