2013-01-22 43 views
1
return new SchoolFees(
     new Percentage(schoolFeesResult.Sum(x => (x.Amount.Value/totalFees) * x.TuitionFee.Value)), 
     new Percentage(schoolFeesResult.Sum(x => (x.Amount.Value/totalFees) * x.TravellingFee.Value)), 
     new Percentage(schoolFeesResult.Sum(x => (x.Amount.Value/totalFees) * x.ResidentialFee.Value))); 

加权平均有没有一种方法我可以schoolFeesResult运行一次,以计算出每个不同类型的费用(TuitionTravellingResidence)的加权平均。基本上我不想让(x.Amount.Value/totalFees)在我的代码中出现3次?操作一次,总结费

回答

2

又一个脑破解

Func<Func<Fee, decimal>, decimal> totalFee = feeSelector => 
    schoolFeesResult.Sum(x => x.Amount.Value/totalFees * feeSelector(x)); 

return new SchoolFees(
    new Percentage(totalFee(f => f.TuitionFee.Value)), 
    new Percentage(totalFee(f => f.TravellingFee.Value)), 
    new Percentage(totalFee(f => f.ResidentialFee.Value)) 
); 

或者更短:

Func<Func<Fee, decimal>, Percentage> percentageOf = feeSelector => 
    new Percentage(schoolFeesResult.Sum(x => 
     x.Amount.Value/totalFees * feeSelector(x))); 

return new SchoolFees(
    percentageOf(f => f.TuitionFee.Value), 
    percentageOf(f => f.TravellingFee.Value), 
    percentageOf(f => f.ResidentialFee.Value) 
); 
+0

+1:很好,我喜欢它。注意:这也将枚举'schoolFeesResult'三次。 –

+0

@DanielHilgarth谢谢!是的,它枚举了三次,但它不错:)顺便说一句,没有什么区别 - 枚举三次(原始代码),但我不计算每个'重量*费用':) –

+0

它会计算每个重量(' x.Amount.Value/totalFees')三次,就像原始代码一样。就像原始代码一样,它只会计算每个单独的“重量*费用”。所以,就计算而言,它是一样的。只是更多的风格:) –

1

我用这个实现的WeightedAverage作为一个扩展的方法来IEnumerable<T>

public static double? WeightedAverage<TSource>(this IEnumerable<TSource> source 
                 , Func<TSource, float> weightField 
                 , Func<TSource, double> propertyToWeight) 
{ 
    var total = source.Sum(weightField); 

    var sum = source.Select(item => weightField(item) * propertyToWeight(item)).Sum(); 
    return sum/total; 

} 

有几个重载处理singlesingle?当然double。也许你可以调整它来适应你想要达到的目标。

1

我想你可以把它放在另一个查询,恕我直言,这也更redable然后:

var percentages = schoolFeesResult 
    .Select(x => new { SFR = x, AmoundDivFees = (x.Amount.Value/totalFees)}) 
    .Select(x => new { 
     TuitionFee = x.AmoundDivFees * x.SFR.TuitionFee.Value, 
     TravellingFee = x.AmoundDivFees * x.SFR.TravellingFee.Value, 
     ResidentialFee = x.AmoundDivFees * x.SFR.ResidentialFee.Value 
    }); 
return new SchoolFees(
    new Percentage(percentages.Sum(x => x.TuitionFee)), 
    new Percentage(percentages.Sum(x => x.TravellingFee)), 
    new Percentage(percentages.Sum(x => x.ResidentialFee))); 

当然,我不能对它进行测试。

3

你可以使用这样的事情:

var fees = from fee in schoolFeesResult 
      let weight = fee.Amount.Value/totalFees 
      select new 
      { 
       TuitionFee = weight * fee.TuitionFee.Value, 
       TravellingFee = weight * fee.TravellingFee.Value, 
       ResidentialFee = weight * fee.ResidentialFee.Value 
      }; 

// if the calculation of the fees is a performance bottleneck, 
// uncomment the next line: 
// fees = fees.ToList(); 

return new SchoolFees(
    new Percentage(fees.Sum(x => x.TuitionFee), 
    new Percentage(fees.Sum(x => x.TravellingFee), 
    new Percentage(fees.Sum(x => x.ResidentialFee)); 

你甚至能走得更远:

var fees = (from fee in schoolFeesResult 
      let weight = fee.Amount.Value/totalFees 
      group fee by 1 into g 
      select new 
      { 
       TuitionFee = g.Sum(x => weight * x.TuitionFee.Value), 
       TravellingFee = g.Sum(x => weight * x.TravellingFee.Value), 
       ResidentialFee = g.Sum(x => weight * x.ResidentialFee.Value) 
      }).Single(); 

return new SchoolFees(
    new Percentage(fees.TuitionFee, 
    new Percentage(fees.TravellingFee, 
    new Percentage(fees.ResidentialFee); 

但我怀疑,这第二个版本是一个好主意。它使代码难以理解。我纯粹出于学术原因添加了它,以展示什么是可能的。

+1

我想你需要'ToList'或每个费将被计算三次 –

+0

@lazyberezovsky:这是正确的。如果这是一个问题,你可以添加'ToList'没有问题。我没有添加它,因为我认为这里没有真正的区别。它会使代码看起来不那么“好”:-)然而,我添加了一条评论,因为它确实是一个重要的观点。 –

+0

Ha-ha :) +1代码。如果代码看起来不错,谁会关心性能? –