Andy Schmitt Andy Schmitt - 24 days ago 16
C# Question

C# get position of object recursively

I have a class that is self-referenced.
IdModuloPai is the key that points to its parent, and ModulosFilhos is the children of that object.

I have a property, Profundidade, that recursively calc the depth of that object.

Another important property is Ordem. It holds the desired order defined by the user in that scope.

Id Nome IdModuloPai Ordem Profundidade OrdemGlobal
1 Root [NULL] 0 0 0
2 Users 1 0 1 1
3 Administration 2 0 2 2
4 Logs 2 1 2 3
5 Customers 1 0 1 4
6 Orders 5 0 2 5


Look at this example table.

I'm trying to create a function similar to Profundidade, that calcs its global position. I'm trying to get the last column OrdemGlobal. Than, I can order objects by OrdemGlobal and it will appear always in the same way in every local I need.

Based in this table, the correct position is

Root
+Users
+Administration
+Logs
+Customers
+Orders


Look that Administration apperars before Logs because Administration has Ordem = 0 and Logs has Ordem = 1

How can I archieve the desired behavior?

Code of my class follows

public class ModuloModel
{
public int Id { get; set; }
public string Nome { get; set; }
public int Ordem { get; set; }
public virtual int Profundidade {
get
{
return GetDepth(this);
}
}

public int? IdModuloPai { get; set; }
public virtual ModuloModel ModuloPai { get; set; }
public virtual ICollection<ModuloModel> ModulosFilhos { get; set; }

private int GetDepth(ModuloModel moduloModel)
{
if (moduloModel == null) return 0;
if (moduloModel.IdModuloPai == null) return 0;
return GetDepth(moduloModel.ModuloPai) + 1;
}
}


EDIT: Improved Question

I'm tryed something like

public virtual int OrdemGlobal
{
get
{
return GetGlobalOrder(this);
}
}

private int GetGlobalOrder(ModuloModel moduloModel)
{
if (moduloModel == null) return 0;
if (moduloModel.ModuloPai == null) return 0;

int smallerSiblings = moduloModel.ModuloPai.ModulosFilhos.Where(x => x.Ordem < moduloModel.Ordem).Count();
return (GetGlobalOrder(moduloModel.ModuloPai) + smallerSiblings + 1;
}


But this is confused, and aren't returning the desired information.

Answer

Here is a IComparer<ModuloModel> that sorts to the order you want.

public class ModuloModelComparer : Comparer<ModuloModel>
{
    public override int Compare(ModuloModel x, ModuloModel y)
    {
        //They are the same node.
        if (x.Equals(y))
            return 0;


        //Find the shared parent
        if (x.Profundidade > y.Profundidade)
        {
            //x is a child of y
            if (x.ModuloPai.Equals(y))
                return 1;
            return Compare(x.ModuloPai, y);
        }
        else if (y.Profundidade > x.Profundidade)
        {
            //y is a child of x
            if (x.Equals(y.ModuloPai))
                return -1;
            return Compare(x, y.ModuloPai);
        }
        else
        {
            //They both share a parent but are not the same node, just compare on Ordem.
            if (x.ModuloPai.Equals(y.ModuloPai))
                return x.Ordem.CompareTo(y.Ordem);

            //They are the same level but have diffrent parents, go up a layer
            return Compare(x.ModuloPai, y.ModuloPai);
        }
    }
}

Here is a test program that uses it

class Test
{
    public static void Main()
    {

        var root = CreateModel(1, "Root", null, 0);
        var users = CreateModel(2, "Users", root, 0);
        var administration = CreateModel(3, "Administration", users, 0);
        var logs = CreateModel(4, "Logs", users, 1);
        var customers = CreateModel(5, "Customers", root, 0);
        var orders = CreateModel(5, "Orders", customers, 0);


        List<ModuloModel> list = new List<ModuloModel> {root, users, administration, logs, customers, orders};

        list.Sort(new ModuloModelComparer());

        foreach (var moduloModel in list)
        {
            Console.WriteLine(moduloModel.Nome);
        }
        Console.ReadLine();
    }

    private static ModuloModel CreateModel(int id, string Nome, ModuloModel moduloPai, int ordem)
    {
        var model = new ModuloModel {Id = id, Nome = Nome, IdModuloPai = moduloPai?.Id, ModuloPai = moduloPai, ModulosFilhos = new HashSet<ModuloModel>(), Ordem = ordem};
        moduloPai?.ModulosFilhos.Add(model);
        return model;
    }
}

Hopefully this is enough to get you on the right track.

Comments