LINQ 学习之路
一、为什么要使用 LINQ
要理解为什么使用 LINQ,先来看下下面的例子
例子:要统计字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次和其出现的的频率。如果用传统的 Sql 语句来写,一定是非常的繁杂,如果用 LINQ 语句来写,效果如下
string strs = "hello word, Hehehe";
var items = strs.Where(c => char.IsLetter(c)) //过滤非字符
.Select(c => char.IsLower(c)) //大小写都转换成小写
.GroupBy(c => c) //根据字母进行分组
.Where(g => g.Count() > 2) //过滤掉出现次数 <=2
.OrderByDescending(g => g.Count()) //按次数排序
.Select(g => new { Char = g.Key, Count = g.Count() });
使用 Linq 来实现,简单、清晰、明了。这里就体现了 LINQ 的一大好处:让数据处理变得简单
二、揭秘 LINQ 方法的背后
了解 LINQ 方法背后做了些什么,可以让我们更好的使用 LINQ。下面以 Wherr 方法为例,写一个我们自己的 MyWhere 方法
LINQ 提供了很多集合的扩展方法,配合 lambda 能简化数据处理
例子:有一个 [3, 15, 88, 4, 77, 42, 8] 的数组,要得到它 >10 的值
int[] num = new int[] { 3, 15, 88, 4, 77, 42, 8 };
//使用原本的Where
IEnumerable<int> result = num.Where(r => r > 10);
foreach (int i in result)
{
Console.WriteLine(i);
});
}
static void Main(string[] args)
{
int[] num = new int[] { 3, 15, 88, 4, 77, 42, 8 };
//使用我们自己写的MyWhere方法
var result2 = MyWhere2(num, r => r > 10);
foreach (int i in result2)
{
Console.WriteLine(i);
}
}
public static IEnumerable<int> MyWhere(IEnumerable<int> items, Func<int, bool> f)
{
List<int> result = new List<int>();
foreach (var i in items)
{
if (f(i) == true)
{
result.Add(i);
}
}
return result;
}
在上述代码中,MyWhere 方法是根据我们的需求来写的一个扩展方法,它要求传入一个IEnumerable 类型的参数和一个 Func<int,bool> 委托类型的参数,在方法中,新建一个 List 集合用于存值,遍历传入的数组,符合条件就存入集合中,最后返回集合。
三、LINQ 的扩展方法
LINQ 关键的功能是提供了集合类的扩展方法,所以实现了 IEnumerable 接口的类都可以使用这些方法,这是方法并不是 IEnumerable 中的方法,而是以扩展方法的存在于System.Linq 命名空间的静态类中
准备一些测试数据
class Employee
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public int Salary { get; set; }
public override string ToString()
{
return $"Id={Id},Name={Name},Age={Age},Gender={Gender},Salary={Salary}";
}
}
Where(数据过滤)
Where 方法是根据条件对数据进行过滤
语法如下:
IEnumerable<Employee> items = list.Where(e => e.Age > 25);
foreach (var item in items)
{
Console.WriteLine(item);
}
Count(获取数据条数)
语法如下:
//返回一个数字,表示指定序列中满足条件的元素个数。
Console.WriteLine(list.Count(e => e.Salary > 5000 || e.Age > 25));
Any(判断是否有一条数据满足条件)
语法如下:
//是否至少有一条数据
//性能比 Count 要高,Count 会遍历所有数据才返回结果,Any 在遍历数据时遇到满足条件的数据就直接返回结果
Console.WriteLine(list.Any(e => e.Salary == 8000));
Console.WriteLine(list.Where(e=>e.Salary>8000).Any());
Single、SingleOrDefault、First、FirstOrDefault(获取一条数据)
语法如下:
//有且只有一条数据,如果没有符合条件的数据或者存在大于一条符合条件的数据,都会报错
Console.WriteLine(list.Single(s => s.Name == "张德开"));
//有且只有一条数据,返回该条数据,否则返回默认值,如果匹配到多条数据,则会报错
Console.WriteLine(list.SingleOrDefault(s => s.Age == 18));
//匹配到多条数据时,返回第一条数据,没有匹配到数据时,则会报错
Console.WriteLine(list.First(e => e.Age > 30));
//匹配到多条数据时,返回第一条数据,否则返回默认值
Console.WriteLine(list.FirstOrDefault(e => e.Age > 30));
OrderBy、OrderByDescending(排序)
语法如下:
//升序排序
IEnumerable<Employee> e = list.OrderBy(s => s.Age);
//降序排序
e = list.OrderByDescending(s => s.Salary);
e = list.OrderByDescending(s => s.Name[s.Name.Length - 1]);
//可以在OrderBy、OrderByDescending后面继续使用ThenBy、ThenByDescending进行多规则排序
e = list.OrderBy(s => s.Age).ThenBy(s => s.Salary);
foreach (var item in e)
{
Console.WriteLine(item);
}
Sike()、Take()(限制结果集)
语法如下:
// Skip(n) 跳过 n 条数据,Take(n) 获取 n 条数据
var items2 = list.Skip(3).Take(2);
items2 = list.Where(e => e.Age >= 25).OrderBy(e => e.Age).Skip(2).Take(3);
foreach (var item in items2)
{
Console.WriteLine(item);
}
聚合函数
Max(最大值)、Min(最小值)、Average(平均值)、Sun(总和)、Count(总数)
语法如下:
//最大年龄
Console.WriteLine(list.Max(e => e.Age));
//最低工资
Console.WriteLine(list.Min(e => e.Salary));
//平均工资
Console.WriteLine(list.Average(e => e.Salary));
//所有人总工资
Console.WriteLine(list.Sum(e => e.Salary));
//女员工数量
Console.WriteLine(list.Count(e => e.Gender == false));
GroupBy(分组)
GroupBy 方法的参数 keySelector 是分组条件表达式,GroupBy 方法的返回值为IGrouping<TKey, TSource> 类型的泛型 IEnumerable。IGrouping 是继承自 IEnumerable的接口,IGrouping 中唯一的成员就是 Key 属性,表示这一组数据的数据项,由于IGrouping 是继承自 IEnumerable 接口的,因此我们依然可以使用 Count、Min、Average等方法对组内数据进行聚合运算
语法如下:
//根据年龄进行分组
IEnumerable<IGrouping<int, Employee>> items3 = list.GroupBy(e => e.Age);
foreach (IGrouping<int, Employee> g in items3)
{
Console.WriteLine(g.Key);
foreach (Employee e in g)
{
Console.WriteLine(e);
}
}
综合案例:
//根据年龄分组,获取每组人数,最大工资,平均工资,用var简化编程
var item3 = list.GroupBy(e => e.Age);
foreach (var g in item3)
{
Console.WriteLine("每组人数:" + g.Count());
Console.WriteLine("最大工资:" + g.Max(e => e.Salary));
Console.WriteLine("平均工资:" + g.Average(e => e.Salary));
foreach (var e in g)
{
Console.WriteLine(e);
}
}
Select(投影)
可以对集合使用 Select 方法进行投影操作,通俗来讲就是把集合中的每一项逐渐转换为另外一种类型,Select 方法的参数是转换的表达式。
语法如下:
//把集合中的每一项转换成另一个类型
var items4 = list.Select(e => e.Age);
//Select 方法把 bool 的 Gender 转换为字符串类型,Select 方法的返回值为 IEnumerable<string> 类型
var items4 = list.Where(e => e.Salary > 5000).Select(e => e.Gender ? "男" : "女");
//根据年龄分组,然后统计各组人数、年龄、最高工资、最低工资
var items4 = list.GroupBy(e => e.Age).Select(s => new { NianLing = s.Key, MaxS = s.Max(e => e.Salary), MinS = s.Min(e => e.Salary), RenShu = s.Count() });
foreach (var e in items4)
{
Console.WriteLine(e.NianLing + ',' + e.MaxS + "," + e.MinS + ',' + e.RenShu);
}
集合转换
语法如下:
// ToList() 将其他类型转换为 List<T> 类型
List<Employee> empList = list.Where(e => e.Age > 25).ToList();
// ToArray() 将其他类型转换为数组
Employee[] empArr = list.Where(e => e.Salary > 5000).ToArray();
链式调用
上述介绍的这些方法,Where、Count、OrderBy、GroupBy 等方法的返回值都是 IEnumerable 类型,因此它们是可以链式调用的
//获取 id>2 的数据,然后按照 Age 分组,并且把分组按照 Age 排序,然后取出前三条,最后再投影取得年龄、人数、平均工资
var items5 = list.Where(e => e.Id > 2).GroupBy(g => g.Age).OrderBy(g => g.Key).Take(3)
.Select(e => new { LianLin = e.Key, renShu = e.Count(), pingJun = e.Average(e => e.Salary) });
foreach (var i in items5)
{
Console.WriteLine(i.pingJun + "," + i.renShu + "," + i.pingJun);
}
四、LINQ 的另一种写法
LINQ 有两种语法,分别为方法语法和查询语法
//方法语法:使用Where、OrderBy、Selsect等扩展方法来查询数据的写法叫做Linq方法语法
var items6 = list.Where(e => e.Salary > 3000).OrderBy(e => e.Age)
.Select(e => new { e.Name, e.Age, XB = e.Gender ? "男" : "女" });
//查询语法:使用 LINQ 声明性查询语法编写的查询语句,在编译代码时,查询语法必须转换为针对.Net公共语言的的调用,这些方法的调用会调用标准查询运算符
var items7 = from e in list
where e.Salary > 3000
orderby e.Age
select new
{
e.Name,
e.Age,
XB = e.Gender ? "男" : "女"
};
方法语法
“方法语法” 并没有发明新的语法,用的都是扩展方法、Lambda 表达式等 C# 中已经存在的语法,而 “查询语法” 则是新的 C# 语法 。编译器会把 “查询语法” 编译成 “方法语法” 形式,因此它们在运行时没有区别。所有的 “查询语法” 都能改写成 “方法语法”,所有的 “方法语法”也能改写成 “查询语法” 。
查询语法
“查询语法” 看起来更加新颖,而且比 “方法语法” 需要写的代码会少一点,但在编写复杂的查询条件的时候,用 “方法语法” 编写的代码会更清晰
五、小练习
练习一
"85,34,23,45,12,67,60" 计算这些数的平均值
string s = "85,34,23,45,12,67,60";
var avg = s.Split(',').Select(e => Convert.ToInt32(e)).Average();
Console.WriteLine(avg);
练习二
统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次的单词和出现的频率
string s = "Hello word Hehehe Hahaha";
var items7 = s.Where(e => char.IsLetter(e)).Select(e => char.ToLower(e)).GroupBy(c => c)
.Select(g => new { g.Key, Count = g.Count() }).OrderBy(g => g.Count).Where(g => g.Count > 2);
foreach (var item in items7)
{
Console.WriteLine(item);
}
LINQ 学习之路的更多相关文章
- [EntLib]微软企业库5.0 学习之路——第一步、基本入门
话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白). 直到去年在 ...
- 微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—下篇
在上一篇文章中, 我介绍了企业库Cryptographer模块的一些重要类,同时介绍了企业库Cryptographer模块为我们提供的扩展接口,今天我就要根据这些 接口来进行扩展开发,实现2个加密解密 ...
- 微软企业库5.0 学习之路——第四步、使用缓存提高网站的性能(EntLib Caching)
首先先补习下企业库的Caching Application Block的相关知识: 1.四大缓存方式,在Caching Application Block中,主要提供以下四种保存缓存数据的途径,分别是 ...
- 微软企业库5.0 学习之路——第二步、使用VS2010+Data Access模块建立多数据库项目
现在我就开始进入学习之路的第二步——Data Access模块,这个模块是企业库中被使用频率最高的模块,它很好的封装了数据库操作应用,为我们进行多数据库系统开发提供了便利,只需更改配置文件就 可以很快 ...
- jQuery学习之路(1)-选择器
▓▓▓▓▓▓ 大致介绍 终于开始了我的jQuery学习之路!感觉不能再拖了,要边学习原生JavaScript边学习jQuery jQuery是什么? jQuery是一个快速.简洁的JavaScript ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- RPC远程过程调用学习之路(一):用最原始代码还原PRC框架
RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在自己系统中,需要从其他系统中进行调用实现,所以在系统间进行数据交互时经常使用. rpc的实现方式有很多,可以通 ...
- webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口
webService学习之路一:讲解了通过传统方式怎么发布及调用webservice webService学习之路二:讲解了SpringMVC和CXF的集成及快速发布webservice 本篇文章将讲 ...
- [精品书单] C#/.NET 学习之路——从入门到放弃
C#/.NET 学习之路--从入门到放弃 此系列只包含 C#/CLR 学习,不包含应用框架(ASP.NET , WPF , WCF 等)及架构设计学习书籍和资料. C# 入门 <C# 本质论&g ...
- Redis——学习之路四(初识主从配置)
首先我们配置一台master服务器,两台slave服务器.master服务器配置就是默认配置 端口为6379,添加就一个密码CeshiPassword,然后启动master服务器. 两台slave服务 ...
随机推荐
- java bean 慎用 is开头isXxx开头的属性,若必须得用,那么一定要记得 idea自动生成的 setter 和 getter会不标准,从而会引起问题,他自动生成后,需要手工再次进行修改,才可使用,要不然有可能引起各种问题
直接上例子: 然后用 Idea 自动生成 getter 和 setter public class XyzBean { //最普通的 private String name; //Boolean类型, ...
- yapi 的分组的理解!
yapi ,分为超级管理员和 分组组长和项目组长: ------------------------------------------------------------------------ 人 ...
- DNS反向解析
一:创建反向解析区 1.和创建正向解析区的步骤类似,选主要区域 这里设置的IP是192.168.1,一直点确定即可 2.指针记录即反向解析记录,记录ip对应的域名,是反向解析区最常用的记录 输入ip和 ...
- Hbase-执行hbase shell命令时提示:ERROR: KeeperErrorCode = NoNode for /hbase/master
1.问题描述 执行hbase shell命令时提示: ERROR: KeeperErrorCode = NoNode for /hbase/master 2.问题原因 这是与因为服务器重启后Hado ...
- 线性SVM决策过程的可视化
线性 SVM 决策过程的可视化 导入模块 from sklearn.datasets import make_blobs from sklearn.svm import SVC import matp ...
- 思维分析逻辑 5 DAY
目录 如何分析 结构分析 对比分析 时间序列 相关性分析 机器学习 报告撰写 报告撰写三原则 标准化报告的组成 AB测试 AB测试流程 AB测试注意事项 如何分析 结构分析 对比分析 对比分析:所有的 ...
- NC20568 [SCOI2012]滑雪与时间胶囊
题目链接 题目 题目描述 a180285非常喜欢滑雪.他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有一编号i(1 ≤ i ≤ N)和一高度Hi.a18 ...
- NC14352 旅行
题目链接 题目 题目描述 小z放假了,准备到R城市旅行,其中这个城市有N个旅游景点.小z时间有限,只能在三个旅行景点进行游玩.小明租了辆车,司机很善良,说咱不计路程,只要你一次性缴费足够,我就带你走遍 ...
- linux 中grep 命令详细使用方法说明
前言在linux命令行中,经常需要对当前获取的一堆数据进行过滤.提取和分析,其中grep命令是其中非常重要的命令之一,比如,在生产环境服务器上,经常使用到下面这个命令 ps -ef | grep ja ...
- 惠普HP519打印机缺色处理记录
打印蓝色缺失, 黑色出墨不均匀 开盖检查, 发现蓝色墨水管路中间有断线, 拆开打印头后, 用随机器配的桔红色吸墨器吸墨. 之后重新开机还是缺色. 检查彩色打印头, 用浅浅的一层热水泡下方喷嘴, 黄色红 ...