-2

How to sum the value of child items from a hierarchical list in a loop.

I need that during the loop in the list the values of the amount and price property contain the sum of these properties of the children.

Below are the two classes to be used to help me solve my problem.

namespace SGP.Dto.Custo
{    
    public class PlanilhaCusto
    {   
        public int id{ get; set; }
        public int parenteId{ get; set; }      
        public string name { get; set; }
        public decimal amount{ get; set; }
        public decimal price{ get; set; }

        public PlanilhaCusto(int pId, int pParenteId, pName, decimal pAmount, decimal pPrice)
        {
            id = pId;
            parentId = pParentId;
            name = pName;
            amount = pAmount;
            price = pPrice;
        }        
    }    
}

namespace SGP.Dto.Custo
{
    public class ShowList
    {
        List<Dto.Custo.PlanilhaCusto> myList = new List<PlanilhaCusto>();

        public void Show()
        {    
            myList.Add(new PlanilhaCusto(1, null, "Projetos", 0, 0));
            myList.Add(new PlanilhaCusto(2, 1, "Arquitetura", 5,10));
            myList.Add(new PlanilhaCusto(3, 1, "Estrutura", 0, 0));
            myList.Add(new PlanilhaCusto(4, 3, "Civil", 1, 50));
            myList.Add(new PlanilhaCusto(5, 3, "Infra", 3, 75));
            myList.Add(new PlanilhaCusto(6, null, "Pessoal", 0, 0));
            myList.Add(new PlanilhaCusto(7, 6, "Mão de Obra", 20, 5700));

            /*In this loop the value of the parent items must be updated 
              (calculated). The hierarchy of the list can be unlimited, 
              like a tree. I tried using a recursive method but I could 
              not do it.*/
            foreach (var itemList in myList)
            {

            }  
        }
    }
}
L. Guthardt
  • 1,990
  • 6
  • 22
  • 44

2 Answers2

0

Assuming the Elements in the list are sorted the way they are in your example, the fastest way to do this should be to iterate through the list in reverse order and add the amount to the list entry with the correct id.

  1. Iterate backwards through the list
  2. When the current element has a parent add amount and amount*price to it

EDIT:

After reading your comment in your source, i assume you already knew what i just wrote.

My approach would be the following:

        for (int i = myList.Count - 1; i > 1; i--)
        {
            var temp = myList.ElementAt(i);
            if (temp.parentId != null)
            {
                var parent = myList.ElementAt(temp.parentId - 1);
                parent.amount += temp.amount;
                parent.price += (temp.amount * temp.price);
            }
        }
jHN
  • 147
  • 7
0

for creating a hierarchical structure, i modified your PlanilhaCusto to resemble a sort of a Node in tree, having both parent and children members, for ease the traverse

public class PlanilhaCusto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? ParentId { get; set; }
    public decimal Amount { get; set; }
    public decimal Price { get; set; }
    public IEnumerable<PlanilhaCusto> Children { get; set; }
}

Given this structure and an initial input

enter image description here

you can build the entire tree structure regardless of how many levels there are relying on some recursion

public IEnumerable<PlanilhaCusto> Descendants(PlanilhaCusto parent, IEnumerable<PlanilhaCusto> source)
{
    var query = from node in source
                where node.ParentId == parent.Id
                let children = Descendants(node, source)
                select new PlanilhaCusto
                {
                    Id = node.Id,
                    ParentId = node.ParentId,
                    Name = node.Name,
                    Amount = node.Amount,
                    Price = node.Price,
                    Children = children,
                };

    return query.ToList();
}

var hierarchy = from node in source
                let children = Descendants(node, source)
                where node.ParentId == null
                select new PlanilhaCusto
                {
                    Id = node.Id,
                    ParentId = node.ParentId,
                    Name = node.Name,
                    Price = node.Price,
                    Children = children,
                };

that would project the initial data source into something similar

enter image description here

and from here you just need to traverse the hierarchy and compose the total price

public decimal? Total(PlanilhaCusto node)
{
    decimal? price = node.Price * node.Amount;
    if (node.Children != null)
    {
        foreach (var child in node.Children)
        {
            price += Total(child);
        }
    }

    return price;
}

var totals = from node in hierarchy
             select new 
             {
                Id = node.Id,
                Name = node.Name,
                Total = Total(node),
             };

enter image description here

Dan Dohotaru
  • 2,809
  • 19
  • 15
  • see the associated gist for the entire implementation https://gist.github.com/dandohotaru/f87a4adf21b8e516cabf67ecfd4674e8 – Dan Dohotaru Dec 06 '17 at 15:45