zezespecial
zezespecial

Reputation: 485

Sorting List by hierarchy

i need to sort a list by a hierarchy, can someone give me a hand? The list looks like this:

        // create your list
        List<Person> persons = new List<Person>();

        // populate it
        persons.Add(new Person("child", "father"));
        persons.Add(new Person("father", "grandfather"));
        persons.Add(new Person("grandfather", "grandgrandfather"));
        persons.Add(new Person("grandgrandfather", null));

I want something like:

I've tryed to implement IComparable in my classe "Person", like this:

public class Person : IComparable<Person>
{
    public String ID { get; set; }
    public String ParentID { get; set; }

    public Person(String id, String pid)
    {
        this.ID = id;
        this.ParentID = pid;
    }

    public Int32 CompareTo(Person right)
    {


        if (this.ID.Equals(right.ID))
            return 0;

        if (this.ParentID == null) return -1;
        if (right.ParentID == null) return 1;


        return this.ParentID.CompareTo(right.ID);
    }

}

but it isn't doing the thing...

Upvotes: 1

Views: 4105

Answers (4)

Pop Catalin
Pop Catalin

Reputation: 62940

You need to compute the dept of items in the hierarchy, and sort the list by dept:

If the following is the person class:

class Person 
{
    public string Name {get; private set;}
    public string Parent {get; private set;}

    public Person(string name, string parent) 
    {
        this.Name = name;
        this.Parent = parent;
    }
}

This is an example of a method that computes the dept of a person in the hierarchy.

int GetDept(List<Person> list, Person person) 
{
    if (person.Parent == null) return 0;
    return GetDept(list, list.First(p => p.Name == person.Parent)) + 1;
}

Then the method can be used to sort the list by dept

List<Person> persons = new List<Person>();

// populate it
persons.Add(new Person("child", "father"));
persons.Add(new Person("father", "grandfather"));
persons.Add(new Person("grandfather", "grandgrandfather"));
persons.Add(new Person("grandgrandfather", null));

var sorted = persons.OrderBy(p => GetDept(persons, p));

foreach(var person in sorded)
    Console.WriteLine("{0} {1} {2}", person.Name, person.Parent, GetDept(persons, p))

This will print:

grandgrandfather null                0
grandfather      grandgrandfather    1
father           grandfather         2
child            father              3

Note that in this example the dept is not computed efficiently as the GetDept method will get itself called again and again and also it uses O(n) lookup over a list. All these can be improved by computing the dept only once for each person and storing it, combined with a more efficient look up mechanism like a dictionary in order to get good performance for large data sets.

Upvotes: 6

N_A
N_A

Reputation: 19897

Your problem is that you don't have a way to determine which is greater if the values are spread too far apart. For example: Your grandfather and child elements will always return -1 since the string "father" is always less than the string "grandfather". Try making your person values into constant int values and then comparing like this:

const int child = 0;
const int father = 1;
const int grandfather = 2;
const int greatgrandfather = 3;

// create your list
List<Person> persons = new List<Person>();

// populate it
persons.Add(new Person(child));
persons.Add(new Person(father));
persons.Add(new Person(grandfather));
persons.Add(new Person(grandgrandfather));

public class Person : IComparable<Person>
{
    public int ID { get; set; }

    public Person(int id)
    {
        this.ID = id;
    }

    public Int32 CompareTo(Person right)
    {
        if (this.ID == right.ID)
            return 0;

        if (this.ID > right.ID) return -1;
        else return 1;
    }
}

Upvotes: 1

competent_tech
competent_tech

Reputation: 44931

This is a data problem. You are trying to compare string values, but there is nothing inherent in your data that provides relative relationships.

I would suggest that you convert your values to an Enum, which could then be easily compared. Here is some pseudo-code that I have not tested, but which should give you an idea:

public class Person : IComparable<Person>
{
        public enum Types: int {
            None,
            Child,
            Father,
            Grandfather,
            GrandGrandFather
        }
    public Types ID { get; set; }
    public Types ParentID { get; set; }

    public Person(Types id, Types pid)
    {
        this.ID = id;
        this.ParentID = pid;
    }

    public Int32 CompareTo(Person right)
    {
        return this.ParentID.CompareTo(right.ID);
    }

}

Upvotes: 0

Upul Bandara
Upul Bandara

Reputation: 5958

You have to modify the logic of your public int CompareTo(Person right) method according to your sorting logic.

For example

if (this.ID == grandgrandfather &&  
        right.ID == grandfather) return 1;


if (this.ID == grandgrandfather &&  
        right.ID == child) return 1;

....... a lot more 

Upvotes: 0

Related Questions