C# 中奇妙的函數–6. 五個序列聚合運算(Sum, Average, Min, Max,Aggregate)

今天,咱們將着眼於五個用於序列的聚合運算。不少時候當咱們在對序列進行操做時,咱們想要作基於這些序列執行某種彙總而後,計算結果。html

 

Enumerable 靜態類的LINQ擴展方法能夠作到這一點 。就像以前大多數的LINQ擴展方法同樣,這些是基於IEnumerable <TSource>序列的操做。app

SUM() - 計算整個序列的總和

它有兩種形式:函數

  • SUM( 
    • 計算整個序列的總值。
    • 源類型必須是如下類型之一: int,long,double,decimalsingle 或這些類型的可空變種(int?, long?, double?…) 。
  • Sum(Func<TSource, Xprojection)
    • 計算序列投影值的和。
    • 從MSDN上,咱們得知X必須是如下類型之一:int,long,double,decimalsingle 或這些類型的可空變種(int?, long?, double?…) 。

 

在這裏請注意幾件事情。測試

 

首先,儘管在C#中支持許多類型,SUM()方法-非投影式-只支持int,long,double,decimalsingle 。  3d

 

1: // 正確

2: double[] data = { 3.14, 2.72, 1.99, 2.32 };

3: var result = data.Sum();

4: 

5: //不支持

6: short[] shortData = { 1, 2, 5, 7 };

7: 

8: // 出現編輯錯誤

9: var shortResult = shortData.Sum();code

還要注意的是,你能夠操做上面這些類型容許Null 的可空值變種。在以前咱們討論過,可爲空的類型能夠是一個棘手的事情,但用SUM()時咱們不用擔憂,由於全部的空值在求和時都排除了 :htm

 

var data = new List<int?> { 1, 3, 9, 13, null, 7, 12, null };
var result = data.Sum();對象

   
第二,投影形式是一個比較有趣和有用的方法:
 

爲了說明這一點,讓咱們假設一個簡單的POCO Employee:blog

 

 

public sealed class Employee

{

public string Name { get; set; }

public double Salary { get; set; }

public short Dependents { get; set; }

}


接口

 


var employees = new List<Employee>

{

new Employee { Name = "Bob", Salary = 35000.00, Dependents = 0 },

new Employee { Name = "Sherry", Salary = 75250.00, Dependents = 1 },

new Employee { Name = "Kathy", Salary = 32000.50, Dependents = 0 },

new Employee { Name = "Joe", Salary = 17500.00, Dependents = 2 },

};

 

而後咱們就可使用投影方式得到Salary 的總值:

 
var totalSalary = employees.Sum(e => e.Salary);
 
雖然投影形式表面上彷佛被限制在了上述的類型裏(int,long,single,double,decimal),可是若是咱們使用lambda表達式或匿名錶達,投影的形式將容許較短的類型:
 
employees.Sum(e => e.Dependents);

employees.Sum(delegate(Employee e) { return e.Dependents; });
 
這是由於lambda表達式和匿名委託的結果能夠自動擴大小數值類型(如 short)到 int。
 

Average() - 返回序列的平均值

Average()方法,就像SUM()同樣,只不過它是用總和除以實際涉及到的項目數。涉及到的是什麼意思?請記住,SUM( )不包括空值 。Average()是將全部非null值求平均。例如:

 

 

var intList = new int?[] { 10, 20, 30, null };



// 返回 20

Console.WriteLine(intList.Average());

 

MIN() - 返回最小的序列值

MIN()擴展方法用於研究序列,並返回從它的最小值  :

  • Min()
    • 查找序列中最小的值。
    • 拋出異常,若是沒有實現IComparableIComparable<T>
    • 拋出異常,若是序列是空的,源類型是值類型。
    • 若是序列是空的,X是引用類型或者Nullable的值類型,則返回 null 。
  • Min(Func<TSource, Xprojection)
    • 返回泛型序列中的最小值
    • 若是 TSource 類型實現 IComparable<T>,則此方法使用該實現比較值。 不然,若是 TSource 類型實現 IComparable,則使用該實現比較值。
    • 拋出異常,若是序列是空的,X是值類型。
    • 若是 TSource 爲引用類型,且源序列爲空或僅包含爲 null 的值,則此函數返回 null。

       

MIN()支持幾乎任何類型,只要該類型實現IComparableIComparable <T>。所以,它是不限制的數值類型,能夠用於任何比較的對象(包括像值類型的DateTime,TimeSpan):

 

 

var shortList = new short[] { 1, 3, 7, 9, -9, 33 };

// 返回 -9

var smallest = shortList.Min();


// 根據家庭成員數量找到最小值 

var minDependents = employees.Min(e => e.Dependents);

 
此外,MIN()不使用泛型約束限制那些支持IComparable 接口的類型參數。相反,它拋出一個運行異常來回應若是序列非空,沒有在它的對象實現IComparable的接口。
所以若是使用咱們的以前定義的Employee類,下面的第一次調用將返回Null(序列爲空),第二次調用會拋出(非空,但不包含IComparable的對象序列) 。
 

var result1 = Enumerable.Empty<Employee>().Min();

var result2 = employees.Min();
 
最後,請注意,對於值類型,若是序列是空,將拋出異常, 因此下面兩個都會拋出異常:
 

var result3 = Enumerable.Empty<int>().Min();

var result4 = Enumerable.Empty<Employee>().Min(e => e.Dependents);
 
 

MAX() - 返回最大的序列值

MAX()MIN()的行爲徹底同樣,只不過它返回最大值,而不是最小值。所以,咱們可使用這些序列中的最大值,或從一個序列的預測最大值:

 
///返回33   
VAR biggestShort = shortList.Max();

//返回75250.0
VAR highestSalary = employees.Max(E => e.Salary);

其餘方面,請參考Min()。

Aggregate() - 序列的自定義累加器

 

有三種形式的Aggregate():

  • Aggregate(Func<TSource, TSource, TSource> function)
    • 適用於一個函數,它接受一個累加器值和下一個值,並返回結果。
    • 值和序列類型是相同的。
    • 種子值是序列中的第一個值。
  • Aggregate(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> function)
    • 應用序列中的一個函數,累加器值和下一個項目,並返回一個結果。
    • 值和序列類型能夠不一樣或相同。
    • 必須提供一個種子值來初始化,將指定的種子值用做累加器初始值。
  • Aggregate(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> function, Func<TAccumulate, TResult> resultProjection)
    • 同上述,將指定的種子值用做累加器的初始值,並使用指定的函數選擇結果值。

這可能看起來至關複雜。只要記住 「此方法的工做原理是對 source 中的每一個元素調用一次 func。 每次調用 func 時,都將傳遞序列中的元素和聚合值(做爲 func 的第一個參數)。 並用 func 的結果替換之前的聚合值。」

 

例如,若是咱們想要作一個序列中的全部數字的乘法:

 

var numbers = new int[] { 1, 3, 9, 2 };

// 使用當前總值乘如下一個數值獲得新的總值
var product = numbers.Aggregate((total, next) => total * next);

最後的值是: 1 X 3 X 9 X 2 = 54。

 

 

下面看看怎麼用更復雜的聚合計算, 可能咱們想獲得這樣一個結果 -- 用每一個僱員的工資除以家庭總人口數(包括他本身),再將這些數相加:

 

var weirdCalculation = employees.Aggregate(0.0, 

(result, next) => result + next.Salary / (next.Dependents + 1));
 
參照上面的Empolyee 定義,獲得的結果是 110458.8333, 爲方便理解請看下面的Excel 表格:
 
ScreenShot009

因此你看,咱們能夠作至關複雜的聚合計算,關鍵是要記住,你所提供的函數留給下一個「元素」,並把它應用到正在運行的「總值」。

 

摘要

四個簡單的和一個可能有點複雜的,這一組功能至關強大!這些方法能夠很容易地對序列進行聚合,使你不須要進行循環和本身計算。他們很運行快也很容易使用,他們很容易閱讀,他們也被全面測試過了。敬請享受!

相關文章
相關標籤/搜索