C#在LINQ中使用GroupBy

一、先准备要使用的类:

1、Person类:

class Person
  {
    public string Name { set; get; }
    public int Age { set; get; }
    public string Gender { set; get; }
    public override string ToString() => Name;
  }

2、准备要使用的List,用于分组(GroupBy):

List<Person> personList = new List<Person>
    {
      new Person
      {
        Name = "P1", Age = 18, Gender = "Male"

      },
      new Person
      {
        Name = "P2", Age = 19, Gender = "Male",
      },
      new Person
      {
        Name = "P2", Age = 17,Gender = "Female",
      }
    };

二、第一种用法:

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);

官方释义:根据指定的键选择器函数对序列中的元素进行分组。

我们要分组的集合为source,集合内每个元素的类型为TSource,这里第一个参数keySelector的类型为Func<TSource, TKey>,用于将TSource元素按照由此委托返回的类型TKey进行分组,结果为一个已分好组的集合(集合中的集合)。

编写客户端试验代码如下:

var groups = personList.GroupBy(p => p.Gender);
    foreach (var group in groups)
    {
      Console.WriteLine(group.Key);
      foreach(var person in group)
      {
        Console.WriteLine($"\t{person.Name},{person.Age}");
      }
    }

以上代码指定的KeySelector是Person类的Gender属性,因此,以上会按照Gender(性别)进行分组,我们使用两个嵌套的foreach循环将分组的内容打印到控制台。

因为groups返回的类型为IEnumerable<IGouping<TKey,TSource>>,因此以上返回的类型为IEnumerable<IGouping<string,Person>>。

IGouping<string,Person>是已经分组后的集合,内部集合元素为Person,且IGouping有一个Key属性,类型为string(指的是Gender属性类型),用于分组的标识。

输出结果如下:

其等价的LINQ语句为:

var groups = from p in personList
       group p by p.Gender;

以上的意思可以这样理解:从personList取出p,并对p进行分组,使用分组的依据(Key)为p.Gender,并将分组的结果存储到pGroup,并将分组的结果选择出来合并成一个集合。

三、第二种用法:

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer);

官方释义:根据指定的键选择器函数对序列中的元素进行分组,并使用指定的比较器对键进行比较。

这种比第一种方法多了一个参数,那就是一个相等比较器,目的是为了当TKey为自定义的类时,GroupBy能根据TKey指定的类根据相等比较器进行分组,

因此,自定义类如何进行分组,GroupBy是不知道的,需要自己定义自己的相等比较器。

首先,将personList更改如下(下划线部分):

List<Person> personList = new List<Person>
    {
      new Person
      {
        Name = "P1", Age = 18, Gender = "Male"

      },
      new Person
      {
        Name = "P1", Age = 19, Gender = "Male",
      },
      new Person
      {
        Name = "P3", Age = 17,Gender = "Female",
      }
    };

其次,增加一个相等比较器类,用于对Person进行分组:

  class PersonEqualityComparer : IEqualityComparer<Person>
  {
    public bool Equals(Person x, Person y) => x.Name == y.Name;
    public int GetHashCode(Person obj) => obj.Name.GetHashCode();
  }

其中定义了如何对一个Person相等性定义,只要实现IEqualityComparer<Person>即可,这里以Name作为Person类是否相同的依据。

最后,现在我们对Person类进行分组,编写客户端实验代码如下:

var groups = personList.GroupBy(p => p, new PersonEqualityComparer());
    foreach (var group in groups)
    {
      Console.WriteLine(group.Key.ToString());
      foreach(var person in group)
      {
        Console.WriteLine($"\t{person.Age},{person.Gender}");
      }
    }

以上的分组依据是Person类,并运用了自己定义的Person类相同比较器,只要Name相同,就分为一组,

输出结果如下:

四、第三种用法:

public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector);

官方释义:根据指定的键选择器函数对序列中的元素进行分组,并且通过使用指定的函数对每个组中的元素进行投影。

这个比第一种用法多了一个elementSelector,第一种用法是对集合本身按照TKey分组,并将自己(TSource)添加到分组内,而当前的用法则可以选择自己想要添加到分组内的元素类型。

编写客户端实验代码如下:

var groups = personList.GroupBy(p => p.Gender, p=>p.Name);
    foreach (var group in groups)
    {
      Console.WriteLine(group.Key.ToString());
      foreach(var name in group)
      {
        Console.WriteLine($"\t{name}");
      }
    }

以上代码是按照p.Gender进行分组,并将p.Name作为组内的元素。

输出结果如下:

其等价的LINQ语句为:

var groups = from p in personList
       group p.Name by p.Gender;

五、第四种用法:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector);

官方释义:根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值。

这个跟之前的用法都不同,之前的用法都是将结果进行分组,并返回IGrouping<TKey,TSource>对象,而当前用法则是返回自己定义的类型(TResult),在返回自己定义类型之前,将会传入两个参数,一个是TKey,为分组时指定的对象,另外一个则是IEnumerable<TSource>,为分组后的内部对象集合。

编写客户端实验代码如下:

string GetPersonInfo(string gender, IEnumerable<Person> persons)
      {
        string result = $"{gender}:\t";
        foreach (var p in persons)
        {
          result += $"{p.Name},{p.Age}\t";
        }
        return result;
      }
      var results = personList.GroupBy(p => p.Gender,(g, ps) => GetPersonInfo(g,ps));
      foreach (var result in results)
      {
        Console.WriteLine(result);
      }

GetPersonInfo为局部方法,见于C#7.0及以上。

以上代码将分组后的内容(一个是TKey,为p.Gender,另外一个是IEnumerable<TSource>,为IEnumerable<Person>)作为字符串输出,因此,将返回的类型为字符串集合。

输出结果如下:

其等价的LINQ语句为:

      var results = from p in personList
             group p by p.Gender into pGroup
             select GetPersonInfo(pGroup.Key, pGroup);

六、第五种用法:

public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer);

官方释义:根据键选择器函数对序列中的元素进行分组。通过使用比较器对键进行比较,并且通过使用指定的函数对每个组的元素进行投影。

与第三种用法基本相同,只是多了一个相等比较器,用于分组的依据。

使用第二种用法的personList及PersonEqualityComparer,编写客户端实验代码如下:

var groups = personList.GroupBy(p => p, p => new { p.Age,p.Gender },new PersonEqualityComparer());
      foreach (var group in groups)
      {
        Console.WriteLine(group.Key.ToString());
        foreach (var name in group)
        {
          Console.WriteLine($"\t{name.Age},{name.Gender}");
        }
      }

以上代码的分组依据是Person,PersonEqualityComparer则是作为Person分组的比较器,每个组内为一个匿名类型集合。

输出结果如下:

七、第六种用法:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector, IEqualityComparer<TKey> comparer);

官方释义:根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值。通过使用指定的比较器对键进行比较。

与第四种用法基本相同,只是多了一个相等比较器,用于分组的依据。

使用第二种用法的personList及PersonEqualityComparer,编写客户端实验代码如下:

string GetPersonInfo(Person person, IEnumerable<Person> persons)
      {
        string result = $"{person.ToString()}:\t";
        foreach (var p in persons)
        {
          result += $"{p.Age},{p.Gender}\t";
        }
        return result;
      }
      var results = personList.GroupBy(p => p, (p, ps) => GetPersonInfo(p, ps),new PersonEqualityComparer());
      foreach (var result in results)
      {
        Console.WriteLine(result);
      }

以上代码的分组依据是Person,PersonEqualityComparer则是作为Person分组的比较器,每个组内为一个Person集合,并将返回类型为string的字符串输出。

输出结果如下:

八、第七种用法:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector);

官方释义:根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值。通过使用指定的函数对每个组的元素进行投影。

与第四种方法很类似,只是对分组内的元素进行选择,原有为TSource,现改为TElement。

编写客户端实验代码如下:

string GetPersonInfo(string gender, IEnumerable<string> names)
      {
        string result = $"{gender}:\t";
        foreach (var name in names)
        {
          result += $"{name}\t";
        }
        return result;
      }
      var results = personList.GroupBy(p => p.Gender, (p=>p.Name) ,(g, ns) => GetPersonInfo(g, ns));
      foreach (var result in results)
      {
        Console.WriteLine(result);
      }

以上代码将使用Gender分组,并将分组后的信息组合成一条字符串,并输出到控制台。

输出结果如下:

九、第八种用法:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer);

官方释义: 根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值。通过使用指定的比较器对键值进行比较,并且通过使用指定的函数对每个组的元素进行投影。

与第七种用法基本相同,只是多了一个相等比较器,用于分组的依据。

使用第二种用法的personList及PersonEqualityComparer,编写客户端实验代码如下:

var results = personList.GroupBy(p => p, (p=>new { p.Age,p.Gender}),
        (p, ns) =>
        {
          string result = $"{p.ToString()}:\t";
          foreach (var n in ns)
          {
            result += $"{n.Age},{p.Gender}\t";
          }
          return result;
        },new PersonEqualityComparer());
      foreach (var result in results)
      {
        Console.WriteLine(result);
      }

以上代码将使用Person分组,使用Person比较器作为分组的依据,并将分组后的信息组合成一条字符串,并输出到控制台。

输出结果如下:

以上就是C#在LINQ中使用GroupBy的详细内容,更多关于C#使用GroupBy的资料请关注我们其它相关文章!

(0)

相关推荐

  • 解决C#中Linq GroupBy 和OrderBy失效的方法

    发现问题 在一个数据列表中我用了Linq GroupBy 和OrderBy. 排序在本机正常使用,发到测试后排序死活不对,总以为是程序问题.于是请教了别人有了以下的答案. 问题原因和解决方法 因为服务器装的是英文版操作系统,没有中文包,所以碰见中文排序无法识别,所以使用OrderBy时需要单独处理下. CultureInfo culture = CultureInfo.GetCultureInfo("zh-cn"); List<TeamDto> teamDtos = tea

  • C# GroupBy的基本使用教程

    起因 今天在公司做一个需求的时候,写的是面条代码,一个方法直接从头写到尾,其中用到了GroupBy,且GroupBy的KeySelector是多个属性而不是单个属性. 但是公司最近推行Clean Code,要让代码有可读性.且作为一个有追求的程序员,肯定是不能写面条代码的,要对代码进行拆分. 重构前GroupBy大概是这样子的: var groups = data.GroupBy(m => new { m.PropertyA, m.PropertyB}) 个人对于短的Linq比较习惯于用方法而不

  • C# 中的GroupBy的动态拼接问题及GroupBy<>用法介绍

    废话不多说了,直接给大家贴代码了,具体代码如下所示: public class Person { public string FirstName{set;get;} public string LastName{set;get;} public Person(){} public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } } List<Person> per

  • C#在LINQ中使用GroupBy

    一.先准备要使用的类: 1.Person类: class Person { public string Name { set; get; } public int Age { set; get; } public string Gender { set; get; } public override string ToString() => Name; } 2.准备要使用的List,用于分组(GroupBy): List<Person> personList = new List<

  • linq中的转换操作符

    这些转换操作符将集合转换成数组:IEnumerable.IList.IDictionary等.转换操作符是用来实现将输入对象的类型转变为序列的功能.名称以"As"开头的转换方法可更改源集合的静态类型但不枚举(延迟加载)此源集合.名称以"To"开头的方法可枚举(即时加载)源集合并将项放入相应的集合类型. 一.AsEnumerable操作符 所有实现了IEnumerable<T>接口的类型都可以调用此方法来获取一个IEnumerable<T>集合

  • 如何在datatable中使用groupby进行分组统计

    本文介绍了在datatable中使用groupby进行分组统计,下面是为大家分享的效果图和实现代码: 实现效果 在SQL中我们可以使用groupby来进行分组统计,如果数据在datatable中该如何使用groupby呢,下面的方法可以实现groupby,代码如下: DataTable dt = new DataTable("cart"); DataColumn dc1 = new DataColumn("areaid", Type.GetType("Sy

  • Python中的groupby分组功能的实例代码

    pandas中的DataFrame中可以根据某个属性的同一值进行聚合分组,可以选单个属性,也可以选多个属性: 代码示例: import pandas as pd A=pd.DataFrame([['Beijing',1.68,2300,'city','Yes'],['Tianjin',1.13,1293,'city','Yes'],['Shaanxi',20.56,3732,'Province','Yes'],['Hebei',18.77,7185,'Province','No'],['Qing

  • Pandas高级教程之Pandas中的GroupBy操作

    目录 简介 分割数据 多index get_group dropna groups属性 index的层级 group的遍历 聚合操作 通用聚合方法 可以同时指定多个聚合方法: NamedAgg 不同的列指定不同的聚合方法 转换操作 过滤操作 Apply操作 简介 pandas中的DF数据类型可以像数据库表格一样进行groupby操作.通常来说groupby操作可以分为三部分:分割数据,应用变换和和合并数据. 本文将会详细讲解Pandas中的groupby操作. 分割数据 分割数据的目的是将DF分

  • linq中的串联操作符

    串联是一个将两个集合连接在一起的过程.在Linq中,这个过程通过Concat操作符实现.Concat操作符用于连接两个集合,生成一个新的集合.来看看Concat操作符的定义: public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) 从方法定义中可以看出:第二个参数为输入一个新的集合,与调用集

  • linq中的连接操作符

    linq中的连接操作符主要包括Join()和GroupJoin()两个. 一.Join()操作符 Join()操作符非常类似于T-SQL中的inner join,它将两个数据源进行连接,根据两个数据源中相等的值进行匹配.例如:可以将产品表和产品类别表进行连接,得到产品名称和与其对应的类型名称.下面看看Join()方法的定义: public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEn

  • linq中的分区操作符

    Linq中的分区指的是在不重新排列元素的情况下,将输入序列划分为两部分,然后返回其中一个部分的操作. 一.Take操作符 Take(int n)表示将从序列的开头返回数量为n的连续元素,常用于分页.其定义如下: public static IEnumerable<TSource> Take<TSource>(this IEnumerable<TSource> source, int count); 该方法只接受一个整数,表示要返回的结果的数量. 看下面的例子: usin

  • Linq中ToList()和CopyToDataTable()用法详解

    最近在项目中使用了Linq,想把Linq的查询结果直接转换成DataTable对象,通过查找发现Linq有一个CopyToDataTable<T>的泛型方法,该方法只能在T是DataRow的情况下使用,发现了这个方法以后就直接在项目中使用了,但是在使用的过程中发现,如果Linq的查询结果不包含任何DataRow对象的时候,使用CopyToDataTable()方法会报错,代码如下: using System; using System.Collections.Generic; using Sy

随机推荐