LINQ
(仅用于学习笔记)
一,为什么学习LINQ
1.为什么要学LINQ?让数据处理变得简单
统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次的单词和其出现的频率。
委托→lambda→LINQ
2.委托是什么
1、委托是可以指向方法的类型,调用委托变量时执行的就是变量指向的方法举例。
using System;
namespace asTest
{
class Program
{
static void Main(string[] args)
{
D1 d = A;
d();
d = B;
d();
D2 d2 = Add;
Console.WriteLine(d2(3, 5));
}
static void A()
{
Console.WriteLine("我是A");
}
static void B()
{
Console.WriteLine("我是B");
}
static int Add(int i,int j)
{
return i + j;
}
}
//声明委托
delegate void D1();
delegate int D2(int i, int j);
}
2、.NET 中定义了泛型委托Action(无返回值)和Func(有返回值),所以一般不用自定义委托类型。举例。
Action a = A;
a();
Func<int,int,int>f = Add;
Console.WriteLine(f(10, 2));
Func<int, int, string> c = C;
Action<int, string> d1 = D;
static string C(int i, int j)
{
return "xxx";
}
static void D(int i,string s)
{
}
二,lambda是怎么来的
1.委托变量不仅可以指向普通方法,还可以指向匿名方法。
2.匿名方法可以写成lambda表达式
3.可以省略参数数据类型,因为编译能根据委托类型推断出参数的类型,用=>引出来方法体。
4.如果委托没有返回值,且方法体只有一行代码,可省{}
5.如果=→>之后的方法体中只有一行代码,且方法有返回值,那么可以省略方法体的以及return。
6. 如果只有一个参数,参数的()可以省略。
static void Main(string []args)
{
//如果委托没有返回值,且方法体只有一行代码,可省{}
Action a1 = delegate ()
{
Console.WriteLine("wwww");
};
a1();
Action a11 = delegate ()=>
Console.WriteLine("wwww");
a11();
Action<string, int> a2 = delegate (string n, int i)
{
Console.WriteLine($"n={n},i={i}");
};
a2("af", 32);
Func<int, int, int> a3 = delegate (int i, int j)
{
return i + j;
};
Console.WriteLine(a3(2, 2));
//lambda表达式
Func<int, int, int> a4 = (int i, int j) =>
{
return i + j;
};
Console.WriteLine(a4(2, 2));
//可以去掉参数
Func<int, int, int> a5 = (i, j) =>
{
return i + j;
};
Console.WriteLine(a5(2, 2));
//如果=→>之后的方法体中只有一行代码,且方法有返回值,那么可以省略方法体的以及return。
Func<int, int, int> a51 = (i, j) =>
i + j;
Console.WriteLine(a51(2, 2));
//如果只有一个参数,参数的()可以省略。
Action<int> a6 = (i) => Console.WriteLine(i);
a6(6);
Action<int> a61 = i => Console.WriteLine(i);
a61(6);
Func<int, bool> a62 = delegate (int i) { return i > 0; };
Console.WriteLine(a62(5));
Func<int, bool> a63 =i=> i > 0;
Console.WriteLine(a63(5));
}
三,LINQ方法的背后
LINQ中提供了很多集合的扩展方法,配合lambda能简化数据处理。
可以使用var让编译器的“类型推断”来简化类型的声明。在LINQ中常用。
C#的var和JavaScript的var不一样,仍然是强类型的。
(*)C#中的弱类型是dynamic。
using System;
using System.Collections.Generic;
using System.Linq;
namespace LInq1
{
class Program
{
static void Main(string[] args)
{
//c#匿名类型
int[] nums = new int[] { 3, 5, 33, 55, 355, 222, 3, 10, 9 };
//where方法会遍历集合中的每个元素,判断
IEnumerable<int> result = nums.Where(a => a > 40);
//使用自定义方法
IEnumerable<int> result = MyWhere1(nums, a => a > 10);
//用var
var result = MyWhere1(nums, a => a > 10);
foreach(int i in result)
{
Console.WriteLine(i);
}
}
//自定义方法
static IEnumerable<int> MyWhere1(IEnumerable<int> items,Func<int,bool>f)
{
List<int> result = new List<int>();
foreach(int i in items)
{
if(f(i)==true)
{
result.Add(i);
}
}
return result;
}
}
}
//第二种方法
static IEnumerable<int> MyWhere2(IEnumerable<int> items, Func<int, bool> f)
{
foreach (int i in items)
{
if (f(i) == true)
{
yield return i;
}
}
}
四,Linq常用扩展方法1
LINQ中提供了大量类似Where的扩展方法,简化数据处理。大部分都在System.Linq命名空间中。
1. 创建一个Employee类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Linq2
{
//准备初始数据,全部代码见备注
class Employee
{
public long ld { 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
$"ld={ld},Name={Name},Age={Age},Gender={Gender},Salary={Salary}";
}
}
}
using System.Collections.Generic;
using System.Linq;
static void Main(string[] args)
{
List<Employee> list = new List<Employee>();
list.Add(new Employee { ld = 1, Name = "jerry", Age = 28, Gender = true, Salary = 5000 });
list.Add(new Employee { ld = 2, Name = "jim", Age = 33, Gender = true, Salary = 3000 });
list.Add(new Employee { ld = 3, Name = "lily", Age = 35, Gender = false, Salary = 9000 });
list.Add(new Employee { ld = 4, Name = "lucy", Age = 16, Gender = false, Salary = 2000 });
list.Add(new Employee { ld = 5, Name = "kimi", Age = 25, Gender = true, Salary = 1000 });
list.Add(new Employee { ld = 6, Name = "nancy", Age = 35, Gender = false, Salary = 8000 });
list.Add(new Employee { ld = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 });
list.Add(new Employee { ld = 8, Name = "jack", Age = 33, Gender = true, Salary = 8000 });
//Where方法
IEnumerable<Employee> items1 = list.Where(e => e.Age > 30);
//
foreach (Employee e in items1)
{
Console.WriteLine(e);
}
//数据总条数量
list.Count();
Console.WriteLine(list.Conunt(e=>e.Age>20));
//判断是否有一条满足
Console.WriteLine(list.Any(e=>e.Salary>3000));
}
五,LinQ常见扩展方法2
获取一条数据(是否带参数的两种写法):
选择合适的方法,“防御性编程”
1.Single:有且只有一条满足要求的数据
//打印整条数据(有且只有一条满足)
//IEnumerable<Employee> items2 = list.Where(e => e.Name == "jerry");
//Employee el = items1.Single();
//Console.WriteLine(el);
//Employee e2 = list.Where(e => e.Name == "jerry").Single();
Employee e2 = list.Single(e => e.Name == "lily");
Console.Write(e2);
2.SingleOrDefault :最多只有一条满足要求的数据
//最多只有一条满足数据的要求SingleOrDefault
Employee e2 = list.SingleOrDefault(e => e.Name == "lily");
Console.WriteLine(e2);
int[] nums = new int[] { 3, 5, 8 };
int i = nums.SingleOrDefault(i => i > 7);
Console.WriteLine(i);
3.First :至少有一条,返回第一条
// 至少有一条,返回第一条;
Employee e2 = list.First(e => e.Age > 20);
Console.WriteLine(e2);
4.FirstOrDefault :返回第一条或者默认值
在这里插入代码片
5.排序
Order()对数据正序排序;
//正序排序
IEnumerable<Employee> items = list.OrderBy(e => e.Age);
foreach(Employee e in items)
{
Console.WriteLine(e);
}
OrderByDescending()倒序排序;
对于简单类型排序,也许不用lambda表达式。
//倒序排序
IEnumerable<Employee> items = list.OrderByDescending(e => e.Age);
foreach (Employee e in items)
{
Console.WriteLine(e);
}
var nums = new int[] { 3, 4, 5, 2, 6, 24, 23 };
IEnumerable<int> nums2 = nums.OrderByDescending(i => i );
foreach(var i in nums2)
{
Console.WriteLine(i);
}
特殊案例:按照最后一个字符排序;
//最后一个字母从大到小
var items = list.OrderByDescending(e => e.Name[e.Name.Length - 1]);
foreach(Employee e in items)
{
Console.WriteLine(e);
}
用Guid或者随机数进行随机排序。
//随机排序
Random rand = new Random();
//var item = list.OrderByDescending(e => Guid.NewGuid());
var item = list.OrderByDescending(e => rand.Next())); ;
foreach (var i in item)
{
Console.WriteLine(i);
}
多排序
//多排序
var items = list.OrderBy(e => e.Age).ThenBy(e=>e.Salary);
foreach (Employee e in items)
{
Console.WriteLine(e);
}
6.限制结果集,获取部分数据
Skip(n)跳过n条数据,Take(n)获取n条数据。
//跳过数据取数据
var items = list.Skip(3).Take(2);
foreach (Employee e in items)
{
Console.WriteLine(e);
}
//链式编程
var item = list.Where(e => e.Age > 30).OrderBy(e => e.Age).Skip(1).Take(2);
foreach (Employee e in item)
{
Console.WriteLine(e);
}
六,Linq常见扩展方法三
聚合函数:
Max()、Min ()、Average ()、Sum ()、Count ()。
LINQ中所有的扩展方法几乎都是针对lEnumerable接口的,而几乎所有能返回集合的都返回IEnumerable,所以是可以把几乎所有方法“链式使用”的。
list.Where(e =>e.Age > 30).Min(e=>e.Salary)
//int a = list.Max(e => e.Age);
//Console.WriteLine(a);
//int a = list.Where(e => e.ld > 6).Max(e=>e.Salary);
//Console.WriteLine(a);
string s = list.Max(e => e.Name);//字符串大小比较算法
Console.WriteLine(s);
//平均值
double a = list.Where(e => e.Age >= 30).Average(e => e.Salary);
Console.WriteLine(a);
分组:
GroupBy()方法参数是分组条件表达式,返回值为IGrouping<TKey, TSource>类型的泛型IEnumerable,也就是每一组以一个
lGrouping对象的形式返回。IGrouping是一个继承自IEnumerable的接口,IGrouping中Key属性表示这一组的分组数据的值。
//分组
IEnumerable<IGrouping<int, Employee>> items = list.GroupBy(e => e.Age);
foreach(IGrouping<int,Employee>g in items)
{
Console.WriteLine(g.Key);
Console.WriteLine("最大工资:" + g.Max(e => e.Salary));
foreach(Employee e in g)
{
Console.WriteLine(e);
}
Console.WriteLine("***********");
}
七,Linq常用扩展方法四
投影:
把集合中的每一项转换为另外一种类型。lEnumerable ages = list.Select(e =>e.Age);
IEnumerable names=
list.Select(e=>e.Gender?“男”:“女”);var dogs = list.Select(p=>new
Dog{NickName=e.Name,Age=e.Age});
//投影
//IEnumerable<int> items = list.Select(e => e.Age);
//foreach(int i in items)
//{
// Console.WriteLine(i);
//}
//IEnumerable<string> items =list.Where(e=>e.Age>30).Select(e => e.Name);
//foreach (string i in items)
//{
// Console.WriteLine(i);
//}
//IEnumerable<string> items = list.Where(e => e.Salary > 5000).Select
// (e => e.Gender ? "男" : "女");
//foreach(string i in items)
//{
// Console.WriteLine(i);
//}
//IEnumerable<Dog> items = list.Select(e => new Dog { NickName = e.Name, Age = e.Salary });
//foreach (Dog i in items)
//{
// Console.WriteLine($"{i.NickName},{i.Age}");
//}
匿名类型:
var p = new {Name=“tom”,ld=1;var p1= new {name,ld=1,p.Agel;通过反编译看匿名类型原理。var的高光时刻!
//匿名类型
Dog dl = new Dog { NickName = "aa" };
var obj1 = new { Name = "ddd", Salary = 3, AAA = "aa", bb = 2 };
Console.WriteLine(obj1.AAA);
投影与匿名类型:
//投影与匿名类型:
//var items=
//list.Select(e => new
//{
// XingMing = e.Name,
// NianLing = e.Age,
// xingbie = e.Gender ? "男" : "女"
//});
//foreach(var ss in items)
//{
// Console.WriteLine(ss.NianLing+ss.xingbie);
//}
IEnumerable<IGrouping<int, Employee>> item = list.GroupBy(e => e.Age);
var item1 = list.GroupBy(e => e.Age).Select(g => new
{
NianLing = g.Key,
MaxS = g.Max(e => e.Salary),
RenShu = g.Count()
})
;
foreach(var i in item1)
{
Console.WriteLine(i.NianLing + "," + i.MaxS + "," + i.RenShu);
}
list.ToLookup(e => e.Name);
八,Linq链式调用
集合转换:
有一些地方需要数组类型或者List类型的变量,我们可以用ToArray()方法和ToList()分别把lEnumerable转换为数组类型和List类型。
//集合转换:
IEnumerable<Employee> itmes = list.Where(e => e.Salary > 6000);
List<Employee> list1 = itmes.ToList();
Employee[] array2 = itmes.ToArray();
链式调用
Where、Select、OrderBy、GroupBy、Take、Skip等返回值都是
lEnumerable类型,所以可以链式调用。例子:“获取ld>2的数据,然后按照Age分组,并且把分组按照Age排序,然后取出前3条,最后再投影取得年龄、人数、平均工资”
//链式调用
//GroupBy:[(key,e)]
//IEnumerable<IGrouping<int,Employee>>
var items = list.Where(e => e.ld > 2).GroupBy(e => e.Age).OrderBy(g => g.Key)
.Take(3)
//g=IGrouping<int,Employee>
.Select(g => new
{
NL = g.Key,
Rs = g.Count(),
PjGz = g.Average(e => e.Salary)
});
foreach (var i in items)
{
Console.WriteLine(i.NL+","+i.Rs+","+i.PjGz);
}
九,Linq语法
查询语法
使用Where、OrderBy、Select等扩展方法进行数据查询的写法叫做“LINQ方法语法”。还有一种“查询语法”…的写法… …
//语法
// var items = list.Where(e => e.Salary > 3000).OrderBy(e => e.Age)
//.Select(e => new { e.Age, e. Name, XB = e.Gender ? "男" : "女" });
var items = from e in list
where e.Salary > 5000
select new
{
e.Age,
e.Name,
XB = e.Gender ? "男" : "女"
};
// foreach(var e in items)
// {
// Console.WriteLine(e);
// }
十,Linq解决简单问题
计算字符串用逗号分隔的数据的平均值
//计算字符串用逗号分隔的平均值
string s = "55,66,11,44,15,46,65";
//方法一
//string[] str = s.Split(',');
//IEnumerable<int> num = str.Select(e => Convert.ToInt32(e));
//double avg = num.Average();
//Console.WriteLine(avg);
//方法二
double avg = s.Split(',').Select(e => Convert.ToInt32(e))
.Average();
Console.WriteLine(avg);
统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出出现频率高于2次的单词和其出现的频率。
string s = "hello wordyy,asf, FASDFASGGASHHHA";
var items= s.Where(c => char.IsLetter(c)).Select(c => char.ToLower(c))
.GroupBy(c => c).Select(g => new { g.Key, Count = g.Count() })
.OrderByDescending(g=>g.Count).Where(g=>g.Count>2);
foreach(var item in items)
{
Console.WriteLine(item);
}