今天,我们将着眼于五个用于序列的聚合运算。很多时候当我们在对序列进行操作时,我们想要做基于这些序列执行某种汇总然后,计算结果。

Enumerable 静态类的LINQ扩展方法可以做到这一点 。就像之前大多数的LINQ扩展方法一样,这些是基于IEnumerable <TSource>序列的操作。

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 。

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();

还要注意的是,你可以操作上面这些类型允许Null 的可空值变种。在之前我们讨论过,可为空的类型可以是一个棘手的事情,但用SUM()时我们不用担心,因为所有的空值在求和时都排除了 :

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

  1. 第二,投影形式是一个比较有趣和有用的方法:
  1.  

为了说明这一点,让我们假设一个简单的POCO Employee:

  1.  

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 },

};

  1.  

然后我们就可以使用投影方式获得Salary 的总值:

  1. var totalSalary = employees.Sum(e => e.Salary);
  1.  
  1. 虽然投影形式表面上似乎被限制在了上述的类型里(intlongsingledoubledecimal),但是如果我们使用lambda表达式或匿名表达,投影的形式将允许较短的类型:
  1.  
  1. employees.Sum(e => e.Dependents);
  2.  
  3. employees.Sum(delegate(Employee e) { return e.Dependents; });
 
  1. 这是因为lambda表达式和匿名委托的结果可以自动扩大小数值类型(如 short)到 int
  1.  

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

Average()方法,就像SUM()一样,只不过它是用总和除以实际涉及到的项目数。涉及到的是什么意思?请记住,SUM( )不包括空值 。Average()是将所有非null值求平均。例如:

  1.  

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

// 返回 20

Console.WriteLine(intList.Average());

  1.  

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):

  1.  

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

// 返回 -9

var smallest = shortList.Min();

// 根据家庭成员数量找到最小值

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

  1. 此外,MIN()不使用泛型约束限制那些支持IComparable 接口的类型参数。相反,它抛出一个运行异常来回应如果序列非空,没有在它的对象实现IComparable的接口。
  1. 因此如果使用我们的之前定义的Employee类,下面的第一次调用将返回Null(序列为空),第二次调用会抛出(非空,但不包含IComparable的对象序列)
  1.  
  1. var result1 = Enumerable.Empty<Employee>().Min();
  2.  
  3. var result2 = employees.Min();
 
最后,请注意,对于值类型,如果序列是空,将抛出异常, 所以下面两个都会抛出异常:
 
  1. var result3 = Enumerable.Empty<int>().Min();
  2.  
  3. var result4 = Enumerable.Empty<Employee>().Min(e => e.Dependents);
  1.  

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

MAX()MIN()的行为完全一样,只不过它返回最大值,而不是最小值。因此,我们可以使用这些序列中的最大值,或从一个序列的预测最大值:

  1.  
  1. ///返回33
    VAR biggestShort = shortList.Max();
  2.  
  3. //返回75250.0
    VAR highestSalary = employees.MaxE => 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 的结果替换以前的聚合值。”

例如,如果我们想要做一个序列中的所有数字的乘法:

  1.  
  1. var numbers = new int[] { 1, 3, 9, 2 };
  2.  
  3. // 使用当前总值乘以下一个数值得到新的总值
    var product = numbers.Aggregate((total, next) => total * next);

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

下面看看怎么用更复杂的聚合计算, 可能我们想得到这样一个结果 -- 用每个雇员的工资除以家庭总人口数(包括他自己),再将这些数相加:

  1. var weirdCalculation = employees.Aggregate(0.0,
  2.  
  3. (result, next) => result + next.Salary / (next.Dependents + 1));
  1.  
  1. 参照上面的Empolyee 定义,得到的结果是 110458.8333, 为方便理解请看下面的Excel 表格:
  1.  

所以你看,我们可以做相当复杂的聚合计算,关键是要记住,你所提供的函数留给下一个“元素”,并把它应用到正在运行的“总值”。

摘要

四个简单的和一个可能有点复杂的,这一组功能相当强大!这些方法可以很容易地对序列进行聚合,使你不需要进行循环和自己计算。他们很运行快也很容易使用,他们很容易阅读,他们也被全面测试过了。敬请享受!

C# 中奇妙的函数–6. 五个序列聚合运算(Sum, Average, Min, Max,Aggregate)的更多相关文章

  1. Hive函数:SUM,AVG,MIN,MAX

    转自:http://lxw1234.com/archives/2015/04/176.htm,Hive分析窗口函数(一) SUM,AVG,MIN,MAX 之前看到大数据田地有关于max()over(p ...

  2. C# 中奇妙的函数–8. String Remove() 和 Replace()

    http://www.cnblogs.com/multiplesoftware/archive/2011/09/27/2192710.html 当对字符串进行操作时,我们经常要删除或者是替换一部分子字 ...

  3. C# 中奇妙的函数–7. String Split 和 Join

    很多时候处理字符串数据,比如从文件中读取或者存入 - 我们可能需要加入分隔符(如CSV文件中的逗号),或使用一个分隔符来合并字符串序列. 很多人都知道使用split()的方法,但使用与其对应的Join ...

  4. C# 中奇妙的函数–String Split 和 Join

    很多时候处理字符串数据,比如从文件中读取或者存入 - 我们可能需要加入分隔符(如CSV文件中的逗号),或使用一个分隔符来合并字符串序列. 很多人都知道使用split()的方法,但使用与其对应的Join ...

  5. 006——php字符串中的处理函数(五)

    <?php /** * 一.addslashes() 在预定义字符串前添加反斜杠 * * stripslashes() 把转义字符串前的反斜杠删除 * get_magic_quotes_gpc( ...

  6. 数组中的reduce 函数理解

    第一次见到reduce 是在js 的高级程序设计中,它的意思是把一个数组减少为一个数,举的例子是数组中元素的求和.它接受一个函数作为参数,函数又有两个参数,一个是prev, 前一个值,一个是next, ...

  7. SQL之开窗函数详解--可代替聚合函数使用

    在没学习开窗函数之前,我们都知道,用了分组之后,查询字段就只能是分组字段和聚合的字段,这带来了极大的不方便,有时我们查询时需要分组,又需要查询不分组的字段,每次都要又到子查询,这样显得sql语句复杂难 ...

  8. (转)笔记320 SQLSERVER中的加密函数 2013-7-11

    1 --SQLSERVER中的加密函数 2013-7-11 2 ENCRYPTBYASYMKEY() --非对称密钥 3 ENCRYPTBYCERT() --证书加密 4 ENCRYPTBYKEY() ...

  9. JavaScript中的普通函数与构造函数比较

    问题 什么是构造函数?构造函数与普通函数区别是什么?用new关键字的时候到底做了什么?构造函数有返回值怎么办?构造函数能当普通函数调用吗? thisthis永远指向当前正在被执行的函数或方法的owne ...

随机推荐

  1. Activity跳转通过EventBus传值问题

    根据阿里发布的Android开发规范:下载地址:https://102.alibaba.com/downloadFile.do?file=1520478361732/Android_v9.pdf Ac ...

  2. LOCK_TIMEOUT

    SET LOCK_TIMEOUT 1000 begin tran TranNameA select * from tablenameA WITH (updlock) where... waitfor  ...

  3. wpf VisualBrush 的使用,可创建重复图像

    VisualBrush 类(msdn) <Grid.Background> <VisualBrush TileMode="Tile" Viewport=" ...

  4. 为DataGridTemplateColumn设置快捷菜单

    <DataGrid.ContextMenu> <ContextMenu> <MenuItem Command="{x:Static ApplicationCom ...

  5. Newtonsoft.Json高级用法之枚举中文转义

    最近看博客园中 焰尾迭的两篇关于"Newtonsoft.Json高级用法"的文章受到了很多人的评论,一度登入到头条推荐. 今天我就不再重复焰尾迭博文中的一些提过的Newtonsof ...

  6. Channel 9视频整理【1】

    David Dong 微软mvp https://www.​facebook.com/​DotNet​Walker http://s​tudyhost.​blogspot.tw/ https://ch ...

  7. VC6下 try catch 在release下的杯具(默认情况下,要加上throw语句catch才不会被优化掉)

    IDE:VC6 今天遇到一个小问题,把我郁闷了好久,××医生的VulEngine不时在wcsstr处发生crash,加了一番强大的参数检查后,再加上了强大的try catch,其实不是很喜欢用try和 ...

  8. Silverlight Virtualization Overview

    当ListBox使用StackPanel作为容器时,如果数据源包含大量数据时,每一条数据都要创建可视化的Item来承载数据,创建这些容器就需要消耗很多时间和内存,另外滚动也非常慢,因为需要计算所有这些 ...

  9. Linux软件安装及基本概念

    apt 基本用法 apt-get [options] install/remove/source 软件包1 [软件包2...] 注意:软件包不要带后缀.deb 常用命令及解释如下: apt下载软件是根 ...

  10. ACM竞赛中的魔方问题专题(不定时更新)

    魔方有6个面,有24中不同的旋转方式: 一般有两种方法: (一):以1面为顶面,向右旋转0,90,180,270度 以2面为顶面,向右旋转0,90,180,270度 ... 以6面为顶面,向右旋转0, ...