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比較器作為分組的依據,並將分組后的信息組合成一條字符串,並輸出到控制台。

輸出結果如下:

 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM