工作中,经常要对各种各样的对象进行各种各样的查询和筛选。在此记录一下,工作中常使用的一些linq语句的用法与实例。以下实例主要涉及到以下三个类。
Car
public class Car
{
/// <summary>
/// 用户id
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 汽车id
/// </summary>
public int CarId { get; set; }
/// <summary>
/// 汽车类型名称
/// </summary>
public string CarName { get; set; }
/// <summary>
/// 品牌名称
/// </summary>
public string BrandName { get; set; }
/// <summary>
/// 价格
/// </summary>
public int Price { get; set; }
/// <summary>
/// 汽车标签
/// </summary>
public List<Tag> Tags { get; set; }
}
Tag
public class Tag
{
/// <summary>
/// 标签ID
/// </summary>
public int TagId { get; set; }
/// <summary>
/// 标签名称
/// </summary>
public string TagName { get; set; }
}
User
public class User
{
/// <summary>
/// 用户id
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; }
}
初始化操作
var tagList1 = new List<Tag>
{
new Tag {TagId = 1, TagName = "高端"},
new Tag {TagId = 2, TagName = "舒适"},
new Tag {TagId = 3, TagName = "宽敞"}
};
var tagList2 = new List<Tag>
{
new Tag {TagId = 4, TagName = "实惠"},
new Tag {TagId = 2, TagName = "舒适"},
new Tag {TagId = 3, TagName = "宽敞"}
};
var carList = new List<Car>
{
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸", Price = 100000, Tags = tagList2, UserId = 2}
};
var userList = new List<User>()
{
new User() {UserId = 1, UserName = "我爱买宝马"},
new User() {UserId = 2, UserName = "我爱买大众"},
new User() {UserId = 3, UserName = "千金少"}
};
Where
Where 操作符用于限定输入集合中的元素,将符合条件的元素组织声称一个序列结果返回IEnumerable。
{
//场景1:在用户选择出UserId等于1的对象
var userInfo = userList.Where(u => u.UserId == 1);
foreach (var item in userInfo)
{
Console.WriteLine(item.UserId+item.UserName);
}
//场景2:在用户中选择述UserId等于2或等于3的对象
var userInfos = userList.Where(u => u.UserId == 2 || u.UserId == 3);
foreach (var item in userInfos)
{
Console.WriteLine(item.UserId+item.UserName);
}
//其他场景:如果有多个筛选条件,加上逻辑运算符往where()里面堆就行
}
WhereIf
where的一个扩展方法,工作中一般在筛选的时候使用,比如给你3,4个条件,如果前提不会空或某个值,则进行筛选。
//场景1:如果筛选条件汽车id不为空时,筛选出汽车id为1的车辆,否则则不筛选。
//如果筛选条件品牌不为空,则筛选品牌为宝马的车辆,否则则不筛选。
//假如carId,和brandName是可空的输入条件。
var carId = 1;
var brandName = "宝马";
carList = carList.WhereIf(carId != null, p => p.CarId == carId)
.WhereIf(brandName != null, p => p.BrandName == brandName).ToList();
foreach (var item in carList)
{
Console.WriteLine(item.CarId+item.BrandName);
}
附 whereIf扩展源码,网上其他博客有更详细的讲解,这里只是普及一下。
public static class LinqExtension
{
public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source,bool condition,Func<T, bool> predicate)
{
return condition ? source.Where(predicate) : source;
}
}
Select
select的意思就是投影,操作符用于根据输入序列中的元素创建相应的输出序列中的元素,输出序列中的元素类型可以与输入序列中的元素类型相同,也可以不同。下面附上一些常用的操作。
//场景1:从userList获取userIdList或者userNameList
var userIdList = userList.Select(s => s.UserId);
var userNameList = userList.Select(s => s.UserName);
Console.WriteLine("*********场景1***********");
foreach (var userIdItem in userIdList)
{
Console.WriteLine(userIdItem);
}
foreach (var uerNameItem in userNameList)
{
Console.WriteLine(uerNameItem);
}
//场景2:从carList中获取出只包含carId和carName
var newCarList1 = carList.Select(c => new { c.CarId, c.CarName });
//甚至你可以往里面加一点判断语句
var newCarList2 = carList.Select(c => new { CarId = c.CarId == 1 ? c.CarId : 0, c.CarName });
//甚至你可以换一个名字
var newCarList3 = carList.Select(c => new { c.CarId, FullName = c.BrandName + c.CarName });
//顺手来一波select和where的联动,获取carid=1的汽车名字
var newCarList4 = carList.Where(c => c.CarId == 1).Select(c => c.CarName);
Console.WriteLine("*********场景2***********");
foreach (var newCarItem in newCarList1)
{
Console.WriteLine(newCarItem.CarId + newCarItem.CarName);
}
foreach (var newCarItem in newCarList2)
{
Console.WriteLine(newCarItem.CarId + newCarItem.CarName);
}
foreach (var newCarItem in newCarList3)
{
Console.WriteLine(newCarItem.CarId + newCarItem.FullName);
}
foreach (var newCarItem in newCarList4)
{
Console.WriteLine(newCarItem);
}
Console.ReadLine();
SelectMany
操作符用于根据输入序列中的每一个元素,在输出序列中创建相应的零个或者多个元素,与Select操作符不同,Select操作符会根据输入序列中的每一个元素创建一个对应的输出序列元素,而SelectMany操作符可以创建多个。
//场景1:直接从一个二维的列表出拉出一组数据。实际上就是双重遍历
//从carList中获取所有tagList的id,并且去重。distinct就是去重
var tagList = carList.SelectMany(c => c.Tags.Select(t => t.TagId)).Distinct();
foreach (var item in tagList)
{
Console.WriteLine(item);
}
Take
操作符用于从输入序列中返回指定数量的元素,常用于分页。
//场景1:直接从一个二维的列表出拉出一组数据。实际上就是双重遍历
//从carList中获取所有tagList的id,并且去重。distinct就是去重
//加上一个take(3)就是去前面三个啦
var tagList = carList.SelectMany(c => c.Tags.Select(t => t.TagId)).Distinct().Take(3);
foreach (var item in tagList)
{
Console.WriteLine(item);
}
TakeWhile
操作符用于从输入序列中返回指定数量且满足一定条件的元素,实际应用的场合个人认为比较少,在这里主要将一下它和where的区别。
//场景1:TakeWhile和Where区别
var carList = new List<Car>
{
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 1", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 1", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 1", Price = 100000, Tags = tagList2, UserId = 2},
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 2", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 2", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 2", Price = 100000, Tags = tagList2, UserId = 2},
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 3", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 3", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 3", Price = 100000, Tags = tagList2, UserId = 2},
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 4", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 4", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 4", Price = 100000, Tags = tagList2, UserId = 2},
};
var carList1 = carList.Where(c => c.CarId == 1);
var carList2 = carList.TakeWhile(c => c.CarId ==1);
Console.WriteLine("where输出");
foreach (var item in carList1)
{
Console.WriteLine(item.CarName);
}
Console.WriteLine("TakeWhile输出");
foreach (var item in carList2)
{
Console.WriteLine(item.CarName);
}
运行结果
相信有些朋友已经看出来了,where相当于一个while循环,会去筛选列表里面所有满足条件的结果。TakeWhile只会选择到满足条件的例子,一旦不满足就会直接跳出循环。有whiele(){if() break;}内味了。
Skip/SkipWhile
操作符用于从输入序列中跳过指定数量的元素,返回由序列中剩余的元素所组成的新序列。操作符用于从输入序列中跳过满足一定条件指定数量的元素,与TakeWhile操作符类似。
//场景1:Skip和SkipWhile区别
var carList = new List<Car>
{
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 1", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 1", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 1", Price = 100000, Tags = tagList2, UserId = 2},
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 2", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 2", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 2", Price = 100000, Tags = tagList2, UserId = 2},
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 3", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 3", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 3", Price = 100000, Tags = tagList2, UserId = 2},
new Car() {CarId = 1, BrandName = "宝马", CarName = "宝马X5 4", Price = 300000, Tags = tagList1, UserId = 1},
new Car() {CarId = 2, BrandName = "宝马", CarName = "宝马X6 4", Price = 400000, Tags = tagList1, UserId = 1},
new Car() {CarId = 3, BrandName = "大众", CarName = "大众朗逸 4", Price = 100000, Tags = tagList2, UserId = 2},
};
//跳过前三条
var carList1 = carList.Skip(3);
//跳过第一条carid=1的车辆,一旦跳过,就算后面再有carid=1的车辆都不再跳过
var carList2 = carList.SkipWhile(s => s.CarId == 1);
Console.WriteLine("Skip输出");
foreach (var item in carList1)
{
Console.WriteLine(item.CarName);
}
Console.WriteLine("SkipWhile输出");
foreach (var item in carList2)
{
Console.WriteLine(item.CarName);
}
Concat/Union
Concat:操作符用于连接两个序列,生成一个新序列。
Union操作符用于将两个序列中的元素合并成一个新的序列,新序列将自动去除重复的元素。(Concat去重版),union在对象之间连接时候,就算对象的属性相同,但是也不会去掉重复的对象,只在基本类型中会去除重复的元素。
//场景1:tagList1和tagList2的拼接
var newTagList1 = tagList1.Concat(tagList2);
var newTagList2 = tagList1.Union(tagList2);
Console.WriteLine("Concat输出");
foreach (var item in newTagList1)
{
Console.WriteLine(item.TagName);
}
Console.WriteLine("Union输出");
foreach (var item in newTagList2)
{
Console.WriteLine(item.TagName);
}
//场景2两个int数组拼接
int[] ints1 = { 2, 4, 9, 3, 0, 5, 1, 7 };
int[] ints2 = { 1, 3, 6, 4, 4, 9, 5, 0 };
IEnumerable<int> intsUnion = ints1.Union(ints2);
IEnumerable<int> intsContact = ints1.Concat(ints2);
Console.WriteLine("Concat输出");
foreach (var item in intsContact)
{
Console.WriteLine(item);
}
Console.WriteLine("Union输出");
foreach (var item in intsUnion)
{
Console.WriteLine(item);
}
OrderBy/OrderByDescending/Reverse
OrderBy:操作符用于对输入序列中的元素进行排序,排序基于一个委托方法的返回值顺序,排序过程完成后,会返回一个类型为IOrderEnumerable<T>的集合对象。
OrderByDescending:操作符的功能与OrderBy操作符基本相同,二者只是排序的方式不同OrderBy是顺序排序,而OrderByDescending则是逆序排序。
Reverse:操作符用于生成一个与输入序列中元素相同,但元素排列顺序相反的新序列。
//场景1:OrderBy/OrderByDescending 升序/降序
var tagList = tagList1.OrderBy(s => s.TagId);
tagList = tagList1.OrderByDescending(s => s.TagId);
Any
确定序列是否包含任何元素是返回true,错误返回false。
//场景1:用any筛选userList中用户拥有的车
var userCarList1 = carList.Where(c => userList.Any(u => u.UserId == c.UserId));
var userCarList2 = carList.FindAll(c => userList.Any(u => u.UserId == c.UserId));
foreach (var item in userCarList1)
{
Console.WriteLine(item.CarName);
}
foreach (var item in userCarList2)
{
Console.WriteLine(item.CarName);
}
这二者输出结果相同,顺带提一下,Where和FindAll的区别,这二者的区别在于Where实现了延时查询(全部查完了再输出),而FindAll是一旦查到一条马上输出一条。运行的结果相同,但是运行的过程不一样。
Distinct
操作符类似于SQL语句中的Distinct语句,这里的Distinct操作符也用于去除一个序列中的重复元素。这里展示一个去掉类中某个相同属性的操作,需要自写一个UserId比较器,具体写法和调用方式如下。
public class UserIdComparer : IEqualityComparer<Car>
{
public bool Equals(Car x, Car y)
{
if (x == null)
return y == null;
return x.UserId == y.UserId;
}
public int GetHashCode(Car obj)
{
return obj.UserId.GetHashCode();
}
}
//场景1:去重userId==1的
var userCarList1 = carList.Distinct(new UserIdComparer());
foreach (var item in userCarList1)
{
Console.WriteLine(item.CarName);
}
GroupBy
对特定的对象进行分组
var carList = StaticData.CarList;
var carGroup = carList.GroupBy(c=>c.UserId);
foreach (var group in carGroup)
{
Console.WriteLine(group.Key);
foreach (var item in group)
{
Console.WriteLine(item.CarName);
}
}
Join/GroupJoin
Join实现内部联接,GroupJoin实现外联接。
Join()方法需要4个参数,第一个参数是指定到要连接的集合,第二个和第三个参数指定联接规则,第四个参数是要选择的结果类型。
var carList = StaticData.CarList;
var userList = StaticData.UserList;
var userCarList=userList.Join(carList,u=>u.UserId,c=>c.UserId,(u,c)=> (
u.UserId,
u.UserName,
c.BrandName,
c.CarName,
c.CarId,
c.Price,
c.Tags
));
GroupJoin外部连接查询
public static class StaticData
{
public static readonly Department[] Departments =
{
new Department() {Name = "Cor", Id = 0},
new Department() {Name = "Hum", Id = 1},
new Department() {Name = "Eng", Id = 2},
new Department() {Name = "Inf", Id = 3},
new Department() {Name = "Phi", Id = 4},
new Department() {Name = "Mar", Id = 5}
};
public static readonly Employee[] Employees =
{
new Employee() { Name="MM",Title="CCN",DepartmentId=0},
new Employee() { Name="MS",Title="SCW",DepartmentId=2},
new Employee() { Name="BJ",Title="EIG",DepartmentId=2},
new Employee() { Name="AB",Title="HD",DepartmentId=1},
new Employee() { Name="PD",Title="EA",DepartmentId=3},
new Employee() { Name="KB",Title="PE",DepartmentId=2},
new Employee() { Name="TH",Title="SA",DepartmentId=2},
new Employee() { Name="EE",Title="PC",DepartmentId=4}
};
}
var departments = StaticData.Departments;
var employees = StaticData.Employees;
var items=departments.GroupJoin(employees,department=> department.Id, employee => employee.DepartmentId,(department, departmentEmployee) => (
department.Id,
department.Name,
departmentEmployee
));
foreach (var item in items)
{
Console.WriteLine(item.Name);
foreach (var employee in item.departmentEmployee)
{
Console.WriteLine(employee);
}
}