如何对LINQ中的所有数字子代属性进行乘法和求和

时间:2019-01-29 08:00:28

标签: c# linq

如何对LINQ中的所有数字子代属性进行乘法和求和

我有如下对象

public class ResourceTier
{
    //primary key. no duplicate
    public int Id { get; set; }
    public int ParentId { get; set; }
    public decimal Volume { get; set; } //has value
    public decimal UnitRate { get; set; } //has value
    public decimal TotalPrice { get; set; } //has no value
}

TotalPrice的默认值为0。这是我要填充该值的属性。 TotalPrice应通过将UnitRateVolume属性相乘,然后将所有子元素(如果有的话)相加来填充。 这是数据

| Id | ParentId | Volume | UnitRate   | TotalPrice  |
| 1  | 0        | -      | -          | 180         |
| 2  | 0        | -      | -          | 30          |
| 3  | 1        | -      | -          | 130         |
| 4  | 1        | 5      | 10         | 50          |
| 5  | 2        | 3      | 10         | 30          |
| 6  | 3        | -      | -          | 50          |
| 7  | 3        | 2      | 40         | 80          |
| 8  | 6        | 4      | 10         | 40          |
| 9  | 6        | 1      | 10         | 10          |

当我尝试此操作时,它不起作用。该代码仅包含直接子项的总和,而不是所有子项(大孙子等)

List<ResourceTier> result = ...;
result.ForEach(x => x.TotalPrice =
        result.Where(c => c.ParentId == x.Id).Count() == 0 ?
        s.UnitRates * s.Volume :
        result.Where(c => c.ParentId == x.Id).Select(s => (s.UnitRates * s.Volume)).Sum());

3 个答案:

答案 0 :(得分:3)

仅使用Linq的解决方案将很难实现,因为它通常仅尊重一个级别的儿童。为了也获得所有孙子孙的总数,您必须完全走树才能获得孩子(和他们的孩子)的总和。

您必须创建一个使用递归的方法,这意味着它将使用一组不同的参数再次调用自身。这样,您可以先获得所有子节点的总数,然后分配当前节点的值,例如:

private decimal SetTotal(IEnumerable<ResourceTier> tiers, ResourceTier current)
{
  current.Total = current.Volume * current.UnitRate;
  // Get children of current node
  var children = tiers.Where(x => x.ParentId == current.Id && x.Id != current.Id);  // The second condition explicitely excludes the current node to avoid infinite loops
  foreach(var child in children)
    current.Total += SetTotal(tiers, child);  // Call method again for children
  return current.Total;
} 

该函数的第一次调用将使用所有没有父项的项目的ID,例如:

foreach(var topLevelNode in tiers.Where(x => x.ParentId == 0))
  SetTotal(tiers, topLevelNode);

请注意,上面的代码应演示递归原理。当然,有更有效的方法可以更快地解决此问题。

答案 1 :(得分:0)

作为其他答案,您需要递归计算总价格

class MyWebErrorHandler extends yii\web\ErrorHandler { 
    use handleExceptionMyWay;
}

并使用此方法:

 public static decimal total(ResourceTier rt, List<ResourceTier> data)
    {
        if (data.Where(x=>x.ParentId==rt.Id).Count()==0)
        return rt.Volume*rt.UnitRate;
        else
        {
          var sum=  data.Where(x => x.ParentId == rt.Id).Select(x=>total( x,data)).Sum();
            return rt.UnitRate*rt.Volume+sum;
        }
    }

输出:

答案 2 :(得分:-1)

LINQ无法更改您的源集合!

但是,您可以创建符合您要求的ResourceTiers的新集合。之后,您可以决定将其分配给输出,或者决定将其保存在数据库,表等中。

IEnumerable<ResourceTier> sourceCollection = ...
IEnumerable<ResourceTier> resourceTiersWithTotalPrice = sourceCollection
    .Select(resourceTier => new ResourceTier
    {
        Id = resourceTier.Id,
        ParentId = resourceTier.ParentId,
        ...
        TotalPrice = resourceTier.Volume * resourceTier.UnitRate,
    });