---
# System prepended metadata

title: Count、Sum、Average、Min、Max
tags: ['LinQ ', ' C# ', ' Aggregate Operators']

---

---
tags: LinQ , C# , Aggregate Operators
---

# Count、Sum、Average、Min、Max
Count、Sum、Average、Min、Max 是 LinQ 內用來進行統計運算(?)的函數. 其與 First 相同 , 都是立即執行（Immediately execution）查詢. 因此不用擔心延遲執行的問題. 另外需要特別注意的是上述函數的回傳值只可能是 **Value Type 以及 Nullable Type .** 也就是說 , 像是回傳學生集合中成績最小的學生物件 , 這個動作是無法達成的.

### [Min](https://docs.microsoft.com/zh-tw/dotnet/api/system.linq.enumerable.min?view=netframework-4.8)
回傳序列中指定項目的最小值. 

常用的多載形式如下(不只)
1. TSource Min<TSource>(this IEnumerable<TSource>)
    - Returns the minimum value in a generic sequence.
2. TResult Min<TSource,TResult>(this IEnumerable<TSource>, Func<TSource,TResult>)	
    - Invokes a transform function on each element of a generic sequence and returns the minimum resulting value.    
#### 使用時機
1. 當集合元素類型為 Value Type , 使用建構式 1 取出集合中的最小值
2. 當集合元素類型為 Reference Type , 使用建構式 2 取出集合中**指定屬性的最小值**.

#### Min 的用處
找出 list 中最小的數字 , 以及 people 中最小的年紀. 
```C#
public static List<int> GetList() => new List<int> { 5, 6, 9, 1, 3 };

public static IEnumerable<(string name, int age)> GetPeople()
{
     yield return (name: "小王", age: 15);
     yield return (name: "老黃", age: 31);
     yield return (name: "阿高", age: 74);
}
```
```C#
static void Main(string[] args)
{     
     var list = GetList();
     var minNum = list.Min();
     Console.WriteLine(minNum);
     
     var people = GetPeople();
     var minAge = people.Min(person => person.age);
     Console.WriteLine(minAge);
     
     Console.ReadKey();
}
```

##### 輸出結果
1. 1
2. 15

#### 簡單實作自己的 Min
不考慮集合中存在 null 的情況.
```C#
public static TSource MyMin<TSource>(this IEnumerable<TSource> source)
{
     if (source == null) throw new Exception("null source");

     var enumerator = source.GetEnumerator();
     TSource value = default;
     if (enumerator.MoveNext())
     {
          Comparer<TSource> comparer = Comparer<TSource>.Default;
          bool hasValue = false;
          do
          {
               if (hasValue == false || comparer.Compare(value, enumerator.Current) > 0)
               {
                    value = enumerator.Current;
                    hasValue = true;
               }
          } while (enumerator.MoveNext());
     }
     return value;
}

public static TResult MyMin<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> func)
{
     if (source == null) throw new Exception(" null source");

     var enumerator = source.GetEnumerator();
     var value = default(TResult);
     if (enumerator.MoveNext())
     {
          var comparer = Comparer<TResult>.Default;
          bool hasValue = false;
          do
          {
               var temp = func(enumerator.Current);
               if (hasValue == false || comparer.Compare(value, temp) > 0)
               {
                    value = temp;
                    hasValue = true;
               }
          } while (enumerator.MoveNext());
     }
     return value;
}
```
##### 假設你希望取得集合中某個屬性值最小的物件 , 或許可以自己寫一個擴充方法. 如下.
```C#
public static TSource MyMin<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
     if (source == null) throw new Exception("null source");

     var enumerator = source.GetEnumerator();
     if (enumerator.MoveNext())
     {
          var comparer = Comparer<TKey>.Default;
          var minKey = selector(enumerator.Current);
          var value = enumerator.Current;
          do
          {
               var key = selector(enumerator.Current);
               if (comparer.Compare(minKey, key) > 0)
               {
                    minKey = key;
                    value = enumerator.Current;
               }
          } while (enumerator.MoveNext());
          return value;
     }
     throw new Exception("no item");
}
```
---

### [Max](https://docs.microsoft.com/zh-tw/dotnet/api/system.linq.enumerable.max?view=netframework-4.8)
回傳序列中指定項目的最大值.

常用的多載形式如下(不只)
1. TSource Max<TSource>(IEnumerable<TSource>)	
    - Returns the maximum value in a generic sequence.
2. TResult Max<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>)	
    - invokes a transform function on each element of a generic sequence and returns the maximum resulting value.
#### 使用時機
1. 當集合元素類型為 Value Type , 使用建構式 1 取出集合中的最大值
2. 當集合元素類型為 Reference Type , 使用建構式 2 取出集合中指定屬性的最大值.
#### Max 的用處
找出 list 中最大的數字 , 以及 people 中最大的年紀. 
```C#
public static List<int> GetList() => new List<int> { 5, 6, 9, 1, 3 };

public static IEnumerable<(string name, int age)> GetPeople()
{
     yield return (name: "小王", age: 15);
     yield return (name: "老黃", age: 31);
     yield return (name: "阿高", age: 74);
}
```
```C#
static void Main(string[] args)
{     
     var list = GetList();
     var maxNum = list.Max();
     Console.WriteLine(minNum);
     
     var people = GetPeople();
     var maxAge = people.Max(person => person.age);
     Console.WriteLine(minAge);
     
     Console.ReadKey();
}
```
##### 輸出結果
1. 9
2. 74
#### 簡單實作自己的 Max
不考慮集合中存在 null 的情況.
```C#
public static TSource MyMax<TSource>(this IEnumerable<TSource> source)
{
     if (source == null) throw new Exception("null source");

     var enumerator = source.GetEnumerator();
     TSource value = default;
     if (enumerator.MoveNext())
     {
          Comparer<TSource> comparer = Comparer<TSource>.Default;
          bool hasValue = false;
          do
          {
               if (hasValue == false || comparer.Compare(value, enumerator.Current) < 0)
               {
                    value = enumerator.Current;
                    hasValue = true;
               }
          } while (enumerator.MoveNext());
     }
     return value;
}

public static TResult MyMax<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> func)
{
     if (source == null) throw new Exception(" null source");

     var enumerator = source.GetEnumerator();
     var value = default(TResult);
     if (enumerator.MoveNext())
     {
          var comparer = Comparer<TResult>.Default;
          bool hasValue = false;
          do
          {
               var temp = func(enumerator.Current);
               if (hasValue == false || comparer.Compare(value, temp) < 0)
               {
                    value = temp;
                    hasValue = true;
               }
          } while (enumerator.MoveNext());
     }
     return value;
}
```

---

### [Sum](https://docs.microsoft.com/zh-tw/dotnet/api/system.linq.enumerable.sum?view=netframework-4.8)
回傳序列中指定項目的總和

我在MSDN文件找不到泛型 TResult 的 API 格式 , 不管 , 反正就記成只有下列兩個形式吧 XD
1. TSource Sum<TSource>(this IEnumerable<TSource>)
2. TResult Sum<TSource,TResult>(this IEnumerable<TSource>, Func<TSource,TResult>)
    - 雖然是這麼寫 , 但實際上此實作是將 TResult 可能的類型都實作一次. 因為 TResult 是泛型呀~~ , 無法做 + 運算 , 除非自己重載運算子 Orz.

#### 使用時機
1. 當集合元素類型為 Value Type , 使用建構式 1 取出集合總和
2. 當集合元素類型為 Reference Type , 使用建構式 2 取出集合中指定屬性的總和.
#### Sum 的用處
找出 list 中所有數字的總和 , 以及 people 中所有年紀的總和. 
```C#
public static List<int> GetList() => new List<int> { 5, 6, 9, 1, 3 };

public static IEnumerable<(string name, int age)> GetPeople()
{
     yield return (name: "小王", age: 15);
     yield return (name: "老黃", age: 31);
     yield return (name: "阿高", age: 74);
}
```
```C#
static void Main(string[] args)
{
     var list = GetList();
     var minNum = list.Sum(num => num);
     Console.WriteLine(minNum);

     var people = GetPeople();
     var minAge = people.Sum(item => item.age);
     Console.WriteLine(minAge);

     Console.ReadKey();
}
```

##### 輸出結果
1. 24
2. 120

#### 簡單實作自己的 Sum
實際上 LinQ 對於 Sum 的實作並沒有使用泛型 , 可能是考慮到效能吧?!
但我不想寫那麼多個 Orz.
```C#
// 實務上不建議用 dynamic 實作.
public static TSource MySum<TSource>(this IEnumerable<TSource> source)
{
     dynamic sum = 0;
     foreach (var item in source)
     {
          sum += item;
     }
     return sum;
}

public static TResult MySum<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
     dynamic sum = 0;
     foreach (var item in source)
     {
          sum += selector(item);
     }
     return sum;
}

// 實際上沒有這個形式
public static TResult MySum<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector, Func<TResult, TResult, TResult> add)
{
     TResult sum = default;
     foreach (var item in source)
     {
          sum = add(sum, selector(item));
     }
     return sum;
}

```
---

### [Average](https://docs.microsoft.com/zh-tw/dotnet/api/system.linq.enumerable.average?view=netframework-4.8)
回傳序列中指定項目的平均

我在MSDN文件也找不到泛型 TResult 的 API 格式 , 一樣不管 , 反正就記成只有下列兩個形式吧 XD

1. double Average<TSource>(this IEnumerable<TSource>)
2. double Average<TSource,TResult>(this IEnumerable<TSource>, Func<TSource,TResult>)
    - 雖然是這麼寫 , 但實際上此實作是將 TResult 可能的類型都實作一次. 因為 TResult 是泛型呀~~ , 無法做 + 運算 , 除非自己重載 Orz.


#### 使用時機
1. 當集合元素類型為 Value Type , 使用建構式 1 取出集合平均
2. 當集合元素類型為 Reference Type , 使用建構式 2 取出集合中指定屬性的平均.
#### Average 的用處
找出 list 中數字的平均 , 以及 people 中年紀的平均. 
```C#
public static List<int> GetList() => new List<int> { 5, 6, 9, 1, 3 };

public static IEnumerable<(string name, int age)> GetPeople()
{
     yield return (name: "小王", age: 15);
     yield return (name: "老黃", age: 31);
     yield return (name: "阿高", age: 74);
}
```
```C#
static void Main(string[] args)
{
     var list = GetList();
     var minNum = list.Average(num => num);
     Console.WriteLine(minNum);

     var people = GetPeople();
     var minAge = people.Average(item => item.age);
     Console.WriteLine(minAge);

     Console.ReadKey();
}
```
##### 輸出結果
1. 4.8
2. 40
#### 簡單實作自己的 Average
```C#
public static double MyAverage<TSource>(this IEnumerable<TSource> source)
{
     dynamic sum = 0;
     int count = 0;
     foreach (var item in source)
     {
          sum += item;
          count++;
     }
     return count > 0 ? 1.0 * sum / count : throw new Exception(" divide zero") ;
}

public static double MyAverage<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
     dynamic sum = 0;
     int count = 0;
     foreach (var item in source)
     {
          sum += selector(item);
          count++;
     }
     return count > 0 ? 1.0 * sum / count : throw new Exception(" divide zero") ;
}
```
---

### [Count](https://docs.microsoft.com/zh-tw/dotnet/api/system.linq.enumerable.count?view=netframework-4.8)
回傳序列中滿足指定條件的元素數量. **指定條件可以是無條件.**
1. int Count<TSource>(IEnumerable<TSource>)	
    - Returns the number of elements in a sequence.
2. int Count<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>)	
    - Returns a number that represents how many elements in the specified sequence satisfy a condition.
#### 使用時機
1. **想知道集合中元素的數量.** 建議使用多載 1 . 另外若是集合的型態有 Count 屬性可以使用 ( 例如 List、Array ) , 則不建議使用 LinQ 的 Count 方法 , 原因是 Count 方法的結果是透過走訪所有集合成員得到.
2. **想要知道集合中滿足指定條件的元素的數量**. 可使用多載 2 .
#### Count 的用處
找出 list 中大於 5 的個數 , 以及 people 的成員個數. 
```C#
public static List<int> GetList() => new List<int> { 5, 6, 9, 1, 3 };

public static IEnumerable<(string name, int age)> GetPeople()
{
     yield return (name: "小王", age: 15);
     yield return (name: "老黃", age: 31);
     yield return (name: "阿高", age: 74);
}
```
```C#
static void Main(string[] args)
{
     var list = GetList();
     var minNum = list.Count(num => num > 5);
     Console.WriteLine(minNum);

     var people = GetPeople();
     var minAge = people.Count();
     Console.WriteLine(minAge);

     Console.ReadKey();
}
```

##### 輸出結果
1. **2** , 比 5 大的數字有 6 以及 9 兩個.
2. **3** , people 有三個人.


#### 簡單實作自己的 Count
```C#
public static int MyCount<TSource>(this IEnumerable<TSource> source)
{
     int count = 0;
     foreach (var item in source)
     {
          checked
          {
               count++;
          }
     }
     return count;
}

public static int MyCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
     int count = 0;
     foreach (var item in source)
     {
          checked
          {
               if (predicate(item))
               {
                    count++;
               }
          }
     }
     return count;
}
```
---

### 總結
LinQ 可以用很簡單的語法解決原本需要好幾次迴圈才能解決的問題.
另外硬要講缺點的話 , 大概是會一直點點點下去 , 會寫得很長.
我自己也還在想有沒有比較好的換行邏輯.

最後用兩個例子作為總結.

#### 想知道不同部門關於薪水的各項統計數據
##### 程式碼
```C#
var data = new[]
{
     new { DepartmentID = "屌炸天", Name = "Gamma", Salary = 42000 },
     new { DepartmentID = "酷到炸", Name = "jeff", Salary = 30000 },
     new { DepartmentID = "屌炸天", Name = "mary", Salary = 25000 },
     new { DepartmentID = "旋風爆", Name = "eric", Salary = 5000 },
     new { DepartmentID = "酷到炸", Name = "lisa", Salary = 65000 },
     new { DepartmentID = "旋風爆", Name = "city", Salary = 95000 },
     new { DepartmentID = "爆炸酷", Name = "linda", Salary = 12500 },
     new { DepartmentID = "爆炸酷", Name = "qoooo", Salary = 35600 },
     new { DepartmentID = "旋風爆", Name = "John", Salary = 53210 },
};

var query = data.GroupBy(item => item.DepartmentID).Select(group =>
                new
                {
                    DepartmentID = group.Key,
                    DepartCount = group.Count(),
                    TotalSalary = group.Sum(groupItem => groupItem.Salary),
                    AverageSalary = group.Average(groupItem => groupItem.Salary),
                    MaxSalary = group.Max(groupItem => groupItem.Salary),
                    MinSalary = group.Min(groupItem => groupItem.Salary),
                    EmployeeNames = group.Select(groupItem => groupItem.Name)
                }
            );

foreach (var item in query)
{
     Console.WriteLine($"部門編號 : {item.DepartmentID} , 部門人數 : {item.DepartCount}");
     Console.WriteLine($"薪水和 : {item.TotalSalary} , 平均薪水 : {item.AverageSalary} , 最高薪水 : {item.MaxSalary} , 最低薪水 : {item.MinSalary}");
     item.EmployeeNames.ToList().ForEach(name => Console.WriteLine($"員工姓名 : {name}  "));
     Console.WriteLine();
}
```
##### 輸出結果
![88iMY5a.png](https://github.com/s0920832252/LinQ-Note/blob/master/Resources/88iMY5a.png?raw=true)

---

#### 薪水的次數分配表
```C#
var data = new[]
{
     new { DepartmentID = "屌炸天", Name = "Gamma", Salary = 42000 },
     new { DepartmentID = "酷到炸", Name = "jeff", Salary = 30000 },
     new { DepartmentID = "屌炸天", Name = "mary", Salary = 25000 },
     new { DepartmentID = "旋風爆", Name = "eric", Salary = 5000 },
     new { DepartmentID = "酷到炸", Name = "lisa", Salary = 65000 },
     new { DepartmentID = "旋風爆", Name = "city", Salary = 95000 },
     new { DepartmentID = "爆炸酷", Name = "linda", Salary = 12500 },
     new { DepartmentID = "爆炸酷", Name = "qoooo", Salary = 35600 },
     new { DepartmentID = "旋風爆", Name = "John", Salary = 53210 },
};

var intervals = new double[] { 80000, 50000, 30000 };
var query = data.GroupBy(employee => intervals.FirstOrDefault(interval => employee.Salary >= interval))
                .Select( groupedDate => new 
                                        { 
                                            groupedDate.Key, 
                                            Count = groupedDate.Count(),
                                        }
                       ).OrderBy(item => item.Key);

foreach (var item in query)
{
     var label = item.Key != 0 ? $"薪水高於{item.Key.ToString()}的人數" : $"薪水低於{intervals[intervals.Length - 1]}的人數";
     Console.WriteLine($"{label} : { item.Count}");
}
```
##### 輸出結果
![vxsFJYd.png](https://github.com/s0920832252/LinQ-Note/blob/master/Resources/vxsFJYd.png?raw=true)

---

### Thank you! 

You can find me on

- [GitHub](https://github.com/s0920832252)
- [Facebook](https://www.facebook.com/fourtune.chen)

若有謬誤 , 煩請告知 , 新手發帖請多包涵

# :100: :muscle: :tada: :sheep: 
