group 子句 (C# 參考)
更新:2007 年 11 月
group 子句會傳回包含零個或多個符合群組之索引鍵值的 IGrouping<TKey, TElement> 物件序列。例如,您可以根據每個字串中的第一個字母來群組字串序列。在此情況下,第一個字母是索引鍵,而且具有 char 的型別,並儲存在每個 IGrouping<TKey, TElement> 物件的 Key 屬性中。編譯器會推斷索引鍵的型別。
您可以在查詢運算式的結尾使用 group 子句,如下列範例所示:
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
from student in students
group student by student.Last[0];
如果您想在每個群組上執行其他查詢作業,可以使用 into 內容關鍵字指定暫時識別項。使用 into 時,您必須繼續進行查詢,最後再以 select 陳述式或其他 group 子句結束查詢,如下列摘錄所示:
// Group students by the first letter of their last name
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery2 =
from student in students
group student by student.Last[0] into g
orderby g.Key
select g;
更多關於搭配和不搭配 into 來使用 group 的完整範例,都提供於本主題的「範例」一節。
列舉群組查詢的結果
因為 group 查詢所產生的 IGrouping<TKey, TElement> 物件基本上是清單的清單,所以您必須使用巢狀的 foreach 迴圈來存取每個群組中的項目。外部迴圈會逐一查看群組索引鍵,而內部迴圈則會逐一查看每個群組本身的每個項目。群組可能只有索引鍵而沒有項目。在前述程式碼範例中,執行查詢的 foreach 迴圈看起來就像這樣:
// Iterate group items with a nested foreach. This IGrouping encapsulates
// a sequence of Student objects, and a Key of type char.
// For convenience, var can also be used in the foreach statement.
foreach (IGrouping<char, Student> studentGroup in studentQuery2)
{
Console.WriteLine(studentGroup.Key);
// Explicit type for student could also be used here.
foreach (var student in studentGroup)
{
Console.WriteLine(" {0}, {1}", student.Last, student.First);
}
}
索引鍵型別
群組索引鍵可以是任何型別,例如字串、內建數值型別,或是使用者定義的具名型別或匿名型別。
依字串群組
前述程式碼範例使用的是 char。您可以輕易改為指定字串索引鍵,例如完整的姓氏。
// Same as previous example except we use the entire last name as a key.
// Query variable is an IEnumerable<IGrouping<string, Student>>
var studentQuery3 =
from student in students
group student by student.Last;
依 bool 群組
下列範例示範如何使用 bool 值做為索引鍵,以便將結果分成兩組。請注意,這些值是由 group 子句中的子運算式所產生。
class GroupSample1
{
// The element type of the data source.
public class Student
{
public string First { get; set; }
public string Last { get; set; }
public int ID { get; set; }
public List<int> Scores;
}
public static List<Student> GetStudents()
{
// Use a collection initializer to create the data source. Note that each element
// in the list contains an inner sequence of scores.
List<Student> students = new List<Student>
{
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {97, 89, 85, 82}}
};
return students;
}
static void Main()
{
// Obtain the data source.
List<Student> students = GetStudents();
// Group by true or false.
// Query variable is an IEnumerable<IGrouping<bool, Student>>
var booleanGroupQuery =
from student in students
group student by student.Scores.Average() >= 80; //pass or fail!
// Execute the query and access items in each group
foreach (var studentGroup in booleanGroupQuery)
{
Console.WriteLine(studentGroup.Key == true ? "High averages" : "Low averages");
foreach (var student in studentGroup)
{
Console.WriteLine(" {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
}
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Low averages
Omelchenko, Svetlana:77.5
O'Donnell, Claire:72.25
Garcia, Cesar:75.5
High averages
Mortensen, Sven:93.5
Garcia, Debra:88.25
*/
依數值範圍群組
下一個範例使用運算式來建立代表百分位數範圍的數值群組索引鍵。請注意,此範例使用 let 當做方便儲存方法呼叫結果的位置,因此您不需要在 group 子句中呼叫方法兩次。另外,在 group 子句中,為了避免「除以零」的例外狀況,程式碼也會進行檢查,以確定學生的平均值不是零。如需如何在查詢運算式中安全地使用方法的詳細資訊,請參閱 HOW TO:處理查詢運算式中的例外狀況 (C# 程式設計手冊)。
class GroupSample2
{
// The element type of the data source.
public class Student
{
public string First { get; set; }
public string Last { get; set; }
public int ID { get; set; }
public List<int> Scores;
}
public static List<Student> GetStudents()
{
// Use a collection initializer to create the data source. Note that each element
// in the list contains an inner sequence of scores.
List<Student> students = new List<Student>
{
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {97, 89, 85, 82}}
};
return students;
}
// This method groups students into percentile ranges based on their
// grade average. The Average method returns a double, so to produce a whole
// number it is necessary to cast to int before dividing by 10.
static void Main()
{
// Obtain the data source.
List<Student> students = GetStudents();
// Write the query.
var studentQuery =
from student in students
let avg = (int)student.Scores.Average()
group student by (avg == 0 ? 0 : avg / 10) into g
orderby g.Key
select g;
// Execute the query.
foreach (var studentGroup in studentQuery)
{
int temp = studentGroup.Key * 10;
Console.WriteLine("Students with an average between {0} and {1}", temp, temp + 10);
foreach (var student in studentGroup)
{
Console.WriteLine(" {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
}
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Students with an average between 70 and 80
Omelchenko, Svetlana:77.5
O'Donnell, Claire:72.25
Garcia, Cesar:75.5
Students with an average between 80 and 90
Garcia, Debra:88.25
Students with an average between 90 and 100
Mortensen, Sven:93.5
*/
依複合索引鍵群組
當您想要依據一個以上的索引鍵來群組項目時,請使用複合索引鍵。您可以使用匿名型別或具名型別來保存索引鍵項目,以便建立複合索引鍵。在下列範例中,請假設類別 Person 已經利用名稱為 surname 和 city 的成員進行宣告。group 子句將會針對每一組姓氏相同且住在同一個城市的學生,建立一個個別的群組。
group person by new {name = person.surname, city = person.city};
如果您必須將查詢變數傳遞到其他方法,請使用命名型別。請使用索引鍵的自動實作屬性建立特殊類別,然後再覆寫 Equals 和 GetHashCode 方法。您也可以使用結構 (Struct),如此您就不一定要覆寫這些方法。如需詳細資訊,請參閱HOW TO:使用自動實作的屬性來實作輕量型類別 (C# 程式設計手冊)和 HOW TO:查詢目錄樹狀結構中的重複檔案 (LINQ)。第二個主題包含程式碼範例,以示範如何搭配命名型別來使用複合索引鍵。
範例
下列範例顯示在未對群組套用其他查詢邏輯的情況下,將來源資料排序的標準模式。這種做法稱為無接續群組。字串陣列中的元素會根據它們的第一個字母來分組。查詢的結果是包含 char 型別之公用 Key 屬性的 IGrouping<TKey, TElement> 型別,以及包含群組中各個項目的 IEnumerable<T> 集合。
group 子句的結果是由多個序列組成的序列。因此,若要存取每個傳回之群組內的個別項目,請依下列範例所示,在反覆查看群組索引鍵的迴圈內使用巢狀 foreach 迴圈。
class GroupExample1
{
static void Main()
{
// Create a data source.
string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" };
// Create the query.
var wordGroups =
from w in words
group w by w[0];
// Execute the query.
foreach (var wordGroup in wordGroups)
{
Console.WriteLine("Words that start with the letter '{0}':", wordGroup.Key);
foreach (var word in wordGroup)
{
Console.WriteLine(word);
}
}
// Keep the console window open in debug mode
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Words that start with the letter 'b':
blueberry
banana
Words that start with the letter 'c':
chimpanzee
cheese
Words that start with the letter 'a':
abacus
apple
*/
本範例示範如何在建立群組之後,搭配使用「接續」(Continuation) 和 into,對群組執行額外的邏輯。如需詳細資訊,請參閱 into (C# 參考)。下列範例會查詢每個群組,並選取其索引鍵值為母音的項目。
class GroupClauseExample2
{
static void Main()
{
// Create the data source.
string[] words2 = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese", "elephant", "umbrella", "anteater" };
// Create the query.
var wordGroups2 =
from w in words2
group w by w[0] into grps
where (grps.Key == 'a' || grps.Key == 'e' || grps.Key == 'i'
|| grps.Key == 'o' || grps.Key == 'u')
select grps;
// Execute the query.
foreach (var wordGroup in wordGroups2)
{
Console.WriteLine("Groups that start with a vowel: {0}", wordGroup.Key);
foreach (var word in wordGroup)
{
Console.WriteLine(" {0}", word);
}
}
// Keep the console window open in debug mode
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Groups that start with a vowel: a
abacus
apple
anteater
Groups that start with a vowel: e
elephant
Groups that start with a vowel: u
umbrella
*/
備註
在編譯時期,group 子句會轉譯成 GroupBy 方法的呼叫。
請參閱
工作
HOW TO:使用各種不同方式分組結果 (C# 程式設計手冊)
HOW TO:針對分組作業執行子查詢 (C# 程式設計手冊)