第十六节:Linq用法大全(四)
1. OfType
获取集合中中指定类型元素。
object[] obj = { 1, 23, 4, 5, 555, "aaa", "bbb" }; int max = obj.OfType<int>().Max(); //结果是55, 获取int类型中的最大值
2. selectMany
相当于二次遍历查找,先遍历一级出来item,再遍历二级item. 和Select的区别,Select只遍历一级。
string[] text = { "Albert was here", "Burke slept late", "Connor is happy" }; var d1 = text.Select(s => s.Split(' ')).ToList(); //3个元素,每个元素里面又有3个 var d2 = text.SelectMany(s => s.Split(' ')).ToList(); //9个元素 var d3 = text.Select(s => s.ToString()).ToList(); //3个元素 var d4 = text.SelectMany(s => s.ToString()).ToList(); //45个元素,一个字母一个元素,空格也算 foreach (var item in d4) { Console.WriteLine(item); }
3. GroupJoin
类似sql中的左连接,linq中join-on-into, 注意:linq中的做外链接操控数据库和直接查出来的集合是不一样,参考集合select的时候要判空,详见下面代码。
代码分享:
1 List<Person> pList = new List<Person> 2 { 3 new Person{ id = 1, pName = "ABC" }, 4 new Person{ id = 2, pName = "EFG" }, 5 new Person{ id = 3, pName = "HIJ"}, 6 new Person{ id = 4, pName = "KLM"}, 7 new Person{ id = 5, pName = "NOP" }, 8 new Person{ id = 6, pName = "QRS"}, 9 new Person{ id = 7, pName = "TUV"} 10 }; 11 List<City> cList = new List<City> 12 { 13 new City{ id = 1,cName = "Guangzhou",uId=1}, 14 new City{ id = 2,cName = "Shenzhen",uId=2 }, 15 new City{ id = 3,cName = "Beijing" ,uId=3}, 16 new City{ id = 4,cName = "Shanghai",uId=4 }, 17 new City{ id = 8,cName = "K4",uId=4 } 18 }; 19 //3.1 先用linq实现一下左连接 20 ////左外连接,需要对右表进行判空 21 ////此处需要特别注意:这里是对应已经查出来的集合进行左外链接,所以需要对右表进行判空处理,如:cName = k == null ? "" : k.cName 22 ////但是这里如果改成直接操作数据库,pList改为db.Person,cList改为db.City, 则无需对右表进行判空,因为它会生成标准left join的sql语句,操控数据库。 23 Console.WriteLine("------------------------下面是linq左连接查询--------------------------"); 24 var result1 = from p in pList 25 join c in cList on p.id equals c.uId into fk 26 from k in fk.DefaultIfEmpty() 27 select new { p.id, p.pName, cName = k == null ? "" : k.cName }; 28 var list1 = result1.ToList(); 29 foreach (var item in list1) 30 { 31 Console.WriteLine($"UserId={item.id},Name={item.pName},CityName={item.cName}"); 32 } 33 //3.2 重点GroupJoin用法 (等价于上面的左外连接) 34 Console.WriteLine("------------------------下面是GroupJoin左连接查询--------------------------"); 35 var result2 = pList.AsQueryable().GroupJoin(cList, p => p.id, c => c.uId, (p, cs) => new 36 { 37 p.id, 38 p.pName, 39 cName = cs.Select(u => u.cName).ToList() 40 }); 41 var list2 = result2.ToList(); 42 foreach (var item in list2) 43 { 44 if (item.cName.Count() != 0) 45 { 46 foreach (var citem in item.cName) 47 { 48 Console.WriteLine($"CityID={item.id},Name={item.pName},CityName={citem}"); 49 } 50 } 51 else 52 { 53 Console.WriteLine($"CityID={item.id},Name={item.pName},CityName="); 54 } 55 }
运行结果:
4. ToLookup
当同一个key要求对应多个value情况ToLookup方法是非常有用的,ToLookup返回一种特殊的数据结构类似我们sql中的group.可以把集合分组并且可以用索引访问这些元素。
注意: Lookup,不像Dictionary, 是不可改变的。 这意味着一旦你创建一个lookup, 你不能对数据源添加或删除元素,即无效,GroupBy后是可以增删的
var products = new List<Product> { new Product {pId = "1", Category = "Electronics", Value = 15.0}, new Product {pId = "2", Category = "Groceries", Value = 40.0}, new Product {pId = "3", Category = "Garden", Value = 210.3}, new Product {pId = "4", Category = "Pets", Value = 2.1}, new Product {pId = "5", Category = "Electronics", Value = 19.95}, new Product {pId = "6", Category = "Pets", Value = 21.25}, new Product {pId = "7", Category = "Pets", Value = 5.50}, new Product {pId = "8", Category = "Garden", Value = 13.0}, new Product {pId = "9", Category = "Automotive", Value = 10.0}, new Product {pId = "10", Category = "Electronics", Value = 250.0}, }; { Console.WriteLine("使用ToLookup分组"); var groups = products.ToLookup(p => p.Category); //删除所有属于Garden的产品(注意ToLookup删除无效哦) products.RemoveAll(p => p.Category == "Garden"); foreach (var group in groups) { Console.WriteLine(group.Key); foreach (var item in group) { Console.WriteLine($"pid={item.pId},Category={item.Category},Value={item.Value}"); } } } { Console.WriteLine("使用GroupBy分组"); var groups = products.GroupBy(p => p.Category); //删除所有属于Garden的产品 products.RemoveAll(p => p.Category == "Garden"); foreach (var group in groups) { Console.WriteLine(group.Key); foreach (var item in group) { Console.WriteLine($"pid={item.pId},Category={item.Category},Value={item.Value}"); } } }
运行结果:
5. AsEnumerable/AsQueryable/Cast
(1).AsEnumerable:是延迟执行的,实际上什么都没有发生,当真正使用对象的时候才执行.
(2).AsQueryable:也是延迟执行的,将一个序列向下转换为一个IQueryable, 它生成了一个本地查询的IQueryable包装.
下面看一下各自linq生成的SQL语句:
(3).Cast:将 IQueryable 的元素转换为指定的类型
List<object> words = new List<object> { "green", "blue", "violet" }; var query1 = words.AsQueryable().Cast<string>().ToList(); //将object类型的list集合转换成string的list集合 //补充写法2 List<string> query = words.Select(u => (string)u).ToList(); //实际上遍历挨个转换
6. SequenceEqual
比较list和list之间、数组和数组之间是否相等。
string[] text1 = { "Albert was here", "Burke slept late", "Connor is happy" }; string[] text2 = { "Albert was here", "Burke slept late", "Connor is happy" }; string[] text3 = { "Albert was here", "Burke slept late", "Connor is happy111" }; string[] text4 = { "Albert was here", "Burke slept late" }; Console.WriteLine(text1.SequenceEqual(text2)); //true Console.WriteLine(text2.SequenceEqual(text3)); //false Console.WriteLine(text2.SequenceEqual(text4)); //false
7. DefaultIfEmpty
返回指定序列的元素;如果序列为空,则返回单一实例集合中的类型参数的默认值。多用于外连接查询。
int[] arr1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };//构造带元素的数组 int[] arr2 = { }; //构造一个空数组 string[] str = { }; var d1 = arr1.DefaultIfEmpty().ToList(); //9个元素 var d2 = arr2.DefaultIfEmpty().ToList(); //1个元素,且为0,即int类型的默认值 var d3 = arr2.DefaultIfEmpty(86).ToList(); //1个元素,且为86,即int类型的默认值 var d4 = str.DefaultIfEmpty().ToList(); //1个元素,且为null,即string类型的默认值
8. Empty
内部生成了一个T[]数组,数组的个数为0。
List<string> list1 = new List<string>(); var arry2 = Enumerable.Empty<string>(); //Enumerable.Empty<int>() 其实在内部生成了一个T[]数组,数组的个数为0。 foreach (var item in list1) { Console.WriteLine(item); } foreach (var item in arry2) { Console.WriteLine(item); }
9. Range
生成指定范围内的整数的序列。如下代码:
var list = Enumerable.Range(10, 100).ToList(); //从10开始,依次添加100个整数,即 10-109 foreach (var item in list) { Console.WriteLine(item); }
10.Repeat
包含一个重复值的序列。
var list = Enumerable.Repeat<int>(10, 100).ToList(); //生成100个10存放在集合中 foreach (var item in list) { Console.WriteLine(item); }
11.Aggregate
做一些复杂的聚合运算,例如累计求和,累计求乘积。它接受2个参数,一般第一个参数是称为累积数(默认情况下等于第一个值),而第二个代表了下一个值。第一次计算之后,计算的结果会替换掉第一个参数,继续参与下一次计算,第二个参数往后顺延。
{ //11.1 累加和阶乘 int[] arry1 = { 1, 2, 3, 4, 5 }; int result1 = arry1.Aggregate((a, b) => { return a + b; }); //执行的操作是: 1+2=3; 3+3=6; 6+4=10; 10+5=15; Console.WriteLine($"累加的结果为:{result1}"); int result2 = arry1.Aggregate((a, b) => { return a * b; }); //执行的操作是: 1*2=2; 2*3=6; 6*4=24; 24*5=120; Console.WriteLine($"阶乘的结果为:{result2}"); //11.2. 实现字符串翻转 string msg1 = "my name is ypf"; string[] arry3 = msg1.Split(' '); string result3 = arry3.Aggregate((m, n) => { return $"{n} {m}"; }); Console.WriteLine($"反转后的结果为:{result3}"); //11.3 求数组中比“banana”长度长的最长字符串 string[] fruits = { "apple", "mango", "orange", "passionfruit", "grape" }; // Determine whether any string in the array is longer than "banana". string longestName = fruits.Aggregate("banana", (longest, next) => { return next.Length > longest.Length ? next : longest; }, fruit => fruit.ToLower()); Console.WriteLine("The fruit with the longest name is {0}.", longestName); }
运行结果:
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。