Ingmar
Ingmar

Reputation: 1447

Flatten a hierarchy as specifically sorted list

I have a simple hierarchy of people (EntityFramework Codefirst).

public class Person
{
    [Key]
    public int Id { get; set; }

    public int? ParentId { get; set; }

    [Required]
    public string Name { get; set; }

    // Navigation

    [ForeignKey("ParentId")]
    public virtual Person Parent { get; set; }

    public virtual ICollection<Person> Children { get; set; }
}

My goal:

I need a method that creates a List<Person> which is (1) clustered by parents and (2) sorted alphabetically by name on each level:

Example output:

Alfred, Christine, Dave and Doris are all parents. They are sorted alphabetically. All of them are having children (except Dave). The children are sorted alphabetically too (and in the list they appear "grouped" with their parent). The "son of" and "daughter of" are just comments for you guys (I don't need this in the resulting list).

I could easily create this kind of list with nested loops. But I am sure this wouldn't be very efficient or elegant. But maybe I am wrong.

Does anyone of you guys have a good idea/suggestion (maybe with LINQ)? THANKS!!

Oh, I should mention that the hierarchy is 2-levels only. So, there is only parents and children (but no grandkids).

Upvotes: 0

Views: 479

Answers (3)

Clint Good
Clint Good

Reputation: 850

var sortedPeople = 
    from p in people
    orderby (p.Parent == null ? p.Name : p.Parent.Name),
      p.Parent == null ? 1 : 2, // this ensures parents appear before children
      p.Name
    select p;

Upvotes: 2

Euphoric
Euphoric

Reputation: 12849

This only works if there are two levels and I don't know how this translates to SQL :

var parents = people.Select(person=>person.Parent == null);
var parentsWithChildrenGrouped = 
    parents
    .SelectMany(parent=>parent.Children.Select(child=>new {Parent = parent, Child = child, Out = child}))
    .Union(parents.Select(parent => new {Parent = parent, Child = null, Out = parent}))
    .OrderBy(x=>x.Parent).ThenBy(x=>x.Child)
    .Select(x=>x.Out);

This query first creates list where each item has both child and it's parent. Then it sorts first based on parent and then by child. Then there is third "out" property that defines what the line is for.

I know this could easily be converted to valid SQL, but I'm not sure how EF is going to handle it.

Upvotes: 1

artm
artm

Reputation: 8584

Would this work?

List<Person> sortPeople(List<Person> people)
{
    List<Person> result = new List<Person>();

    foreach (Person p in people.OrderBy(_ => _.Name).ToList())
    {
        result.Add(p);
        result.AddRange(p.Children.OrderBy(_ => _.Name).ToList());
    }

    return result;
}

Upvotes: 2

Related Questions