要根据一个关键字值对查询结果分组,可以使用 group 子句。
现在要实现一级方程式冠军应按照国家分组,并列出每个国家的冠军数。
子句 group r by r.Country into g 根据Country 属性组合所有的赛手,并定义一个新的 标识符 g,它以后用于访问分组的结果信息。
group 子句的结果根据应用到分组结果上的扩展方法 Count()来排序,
如果冠军数相同,就根据关键字来排序,该关键字是国家,因为这是分组所使用的关键字。
where 子句根据至少有两项的分组来过滤,
select 子句创建一个带Country 和 Count 属性的匿名类型为结果。
private static void Grouping() { var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key //如果冠军数相同,就根据关键字来排序,该关键字是国家,因为这是分组所使用的关键字。 where g.Count() >= 2 select new { Country = g.Key, Count = g.Count() }; foreach (var item in countries) { Console.WriteLine("{0,-10} {1}", item.Country, item.Count); } }
要用扩展方法执行相同的操作,应把groupby 子句解析为GroupBy()方法。
在GroupBy()方法的声明中,注意它返回实现了IGrouping 接口的对象枚举。
IGrouping 接口定义了Key 属性,所以在定义了对这个方法的调用后,可以访问分组的关键字:
public static IEnumerable < IGrouping < TKey, TSource > > GroupBy < TSource, TKey > (
this IEnumerable < TSource > source, Func < TSource, TKey > keySelector);
子句group r by r.Country into g 解析为GroupBy(r => r.Country),返回分组系列。分组系列首先用
OrderByDescending()方法排序,再用ThenBy()方法排序。接着调用Where()和Select()方法。
private static void Grouping() { var countries = Formula1.GetChampions(). GroupBy(r => r.Country). OrderByDescending(g => g.Count()). ThenBy(g => g.Key). Where(g => g.Count() >= 2).
Select(g => new { Country = g.Key, Count = g.Count() }); foreach (var item in countries) { Console.WriteLine("{0,-10} {1}", item.Country, item.Count); } }
如果在分组的对象中包含了嵌套的对象,可以通过改变select 子句创建的匿名类型来实现。
在下面的例子中,所创建的国家不仅应包含国家名和赛手数量这两个属性,还包含赛手名序列。
这个序列用一个赋予Racers 属性的 from in 内部子句指定,
内部的from 子句使用分组标识符g 获得该分组中的所有赛手,用姓氏对它们排序,
再根据姓名创建一个新字符串:
private static void GroupingWithNestedObjects() { var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() >= 2 select new { Country = g.Key, Count = g.Count(),
//在此之前与前面是一样的,下面是以每个分组结果的g为基础进行排序和查询,好象SQL的嵌套,然后成为匿名对象的一个属性 Racers = from r1 in g orderby r1.LastName select r1.FirstName + " " + r1.LastName }; foreach (var item in countries) { Console.WriteLine("{0, -10} {1}", item.Country, item.Count); foreach (var name in item.Racers) //匿名对象的新属性 { Console.Write("{0}; ", name); } Console.WriteLine(); } }
结果:
第二个嵌套分组的例子和SQL的嵌套结果一样,先通过查询计算出结果后,再进行一次查询
private static void GroupingAndAggregation() { var countries = from c in from r in Formula1.GetChampions() group r by r.Country into g select new { Country = g.Key, Wins = (from x in g select x.Wins).Sum() // 注意括号后的调用 }//获得的Key和获胜合计两个属性的匿名对象做源 orderby c.Wins descending //再进行排序 select c; foreach (var item in countries) { Console.WriteLine(item); } }