2013-01-22 72 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代碼。如果代碼看起來不錯,誰會關心性能? –