zoukankan      html  css  js  c++  java
  • [C#] LINQ之GroupBy

    声明:本文为www.cnc6.cn原创,转载时请注明出处,谢谢!

              本文作者文采欠佳,文字表达等方面不是很好,但实际的代码例子是非常实用的,请作参考。

    一、先准备要使用的类:

    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($"	{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($"	{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($"	{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}:	";
                    foreach (var p in persons)
                    {
                        result += $"{p.Name},{p.Age}	";
                    }
                    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($"	{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()}:	";
                    foreach (var p in persons)
                    {
                        result += $"{p.Age},{p.Gender}	";
                    }
                    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}:	";
                    foreach (var name in names)
                    {
                        result += $"{name}	";
                    }
                    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()}:	";
                        foreach (var n in ns)
                        {
                            result += $"{n.Age},{p.Gender}	";
                        }
                        return result;
                    },new PersonEqualityComparer());
                foreach (var result in results)
                {
                    Console.WriteLine(result);
                }

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

    输出结果如下:

  • 相关阅读:
    mysql完全卸载教程(图文详细)
    windows:安装django
    01 Java的NIO三大组件以及buffer的原理以及应用
    16 JDK8的concurrenthashmap的原理介绍
    07 Java源码字节码层面简单分析
    06 Java字节码的基础知识
    05 Java的class文件的组成介绍
    04 G1垃圾回收器的介绍以及垃圾回收调优的基础知识和简单案例
    03 JVM中垃圾回收算法以及典型的垃圾回收器
    02 Java的引用类型以及应用场景
  • 原文地址:https://www.cnblogs.com/cncc/p/9846390.html
Copyright © 2011-2022 走看看