方法 : さまざまな方法で結果をグループ化する (C# プログラミング ガイド)
更新 : 2007 年 11 月
グループ化は、LINQ の最も強力な機能の 1 つです。以下の例では、データをグループ化する各種の方法を示します。
1 つのプロパティ
文字列プロパティの先頭の文字
計算済みの数値の範囲
ブール型の述語またはその他の式
複合キー
また、最後の 2 つのクエリでは、学生の姓と名のみを含む新しい匿名型に結果が格納されます。詳細については、「group 句 (C# リファレンス)」を参照してください。
使用例
このトピックのすべての例で、次のヘルパー クラスとデータ ソースが使用されています。
public class StudentClass
{
#region data
protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
protected class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
public GradeLevel Year;
public List<int> ExamScores;
}
protected static List<Student> students = new List<Student>
{
new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
};
#endregion
//Helper method
protected static int GetPercentile(Student s)
{
double avg = s.ExamScores.Average();
return avg > 0 ? (int)avg / 10 : 0;
}
public void QueryHighScores(int exam, int score)
{
var highScores = from student in students
where student.ExamScores[exam] > score
select new {Name = student.FirstName, Score = student.ExamScores[exam]};
foreach (var item in highScores)
{
Console.WriteLine("{0,-15}{1}", item.Name, item.Score);
}
}
}
public class Program
{
public static void Main()
{
StudentClass sc = new StudentClass();
sc.QueryHighScores(1, 90);
// Keep the console window open in debug mode
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
グループ化キーとして要素の 1 つのプロパティを使用してソース要素をグループ化する方法を次の例に示します。この例では、キーは string です。部分文字列をキーとして使用することもできます。このグループ化操作では、型の既定の等値比較演算子が使用されています。
private static void GroupBySingleProperty()
{
Console.WriteLine("Group by a single property in an object");
// queryLastNames is an IEnumerable<IGrouping<string, DataClass.Student>>
// var is easier to type.
var queryLastNames =
from student in students
group student by student.LastName into newGroup
orderby newGroup.Key
select newGroup;
foreach (var nameGroup in queryLastNames)
{
Console.WriteLine("Key: {0}", nameGroup.Key);
foreach (var student in nameGroup)
{
Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
}
}
}
/* Output:
Group by a single property in an object
Key: Feng
Feng, Hanying
Key: Garcia
Garcia, Hugo
Garcia, Cesar
Garcia, Debra
Key: Mortensen
Mortensen, Sven
Key: O'Donnell
O'Donnell, Claire
Key: Omelchenko
Omelchenko, Svetlana
Key: Tucker
Tucker, Michael
Tucker, Lance
*/
グループ化キーとしてオブジェクトのプロパティ以外のものを使用してソース要素をグループ化する方法を次の例に示します。
private static void GroupBySubstring()
{
Console.WriteLine("\r\nGroup by something other than a property of the object:");
var queryFirstLetters =
from student in students
group student by student.LastName[0];
foreach (var studentGroup in queryFirstLetters)
{
Console.WriteLine("Key: {0}", studentGroup.Key);
// Nested foreach is required to access group items
foreach (var student in studentGroup)
{
Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
}
}
}
/* Output:
Group by first character:
Key: O
Omelchenko, Svetlana
O'Donnell, Claire
Key: G
Garcia, Hugo
Garcia, Cesar
Garcia, Debra
Key: M
Mortensen, Sven
Key: T
Tucker, Michael
Tucker, Lance
Key: F
Feng, Hanying
*/
グループ化キーとして数値の範囲を使用してソース要素をグループ化する方法を次の例に示します。このクエリでは、姓と名および学生が属するパーセンタイルの範囲のみを含む匿名型に結果が格納されます。結果を表示するのに完全な Student オブジェクトを使用する必要がないため、匿名型が使用されています。GetPercentile は、学生の平均点に基づきパーセンタイルを計算するヘルパー関数です。
static int GetPercentile(Student s)
{
double avg = s.Scores.Average();
return avg > 0 ? (int)avg / 10 : 0;
}
private static void GroupByRange()
{
Console.WriteLine("\r\nGroup by numeric range and project into a new anonymous type:");
var queryNumericRange =
from student in students
let percentile = GetPercentile(student)
group new { student.FirstName, student.LastName } by percentile into percentGroup
orderby percentGroup.Key
select percentGroup;
// Nested foreach required to iterate over groups and group items.
foreach (var studentGroup in queryNumericRange)
{
Console.WriteLine("Key: {0}", (studentGroup.Key * 10));
foreach (var item in studentGroup)
{
Console.WriteLine("\t{0}, {1}", item.LastName, item.FirstName);
}
}
}
/* Output:
Group by numeric range and project into a new anonymous type:
Key: 60
Garcia, Debra
Key: 70
Omelchenko, Svetlana
O'Donnell, Claire
Key: 80
Garcia, Hugo
Mortensen, Sven
Garcia, Cesar
Feng, Hanying
Tucker, Lance
Key: 90
Tucker, Michael
*/
ブール型の比較式を使用してソース要素をグループ化する方法を次の例に示します。前の例と同様、完全なソース要素は必要ないため、結果は匿名型に格納されます。匿名型のプロパティは Key メンバのプロパティになること、およびクエリの実行時に名前でアクセスできることに注意してください。
private static void GroupByBoolean()
{
Console.WriteLine("\r\nGroup by a boolean into two groups with string keys");
Console.WriteLine("\"True\" and \"False\" and project into a new anonymous type:");
var queryGroupByAverages = from student in students
group new { student.FirstName, student.LastName }
by student.ExamScores.Average() > 75 into studentGroup
select studentGroup;
foreach (var studentGroup in queryGroupByAverages)
{
Console.WriteLine("Key: {0}", studentGroup.Key);
foreach (var student in studentGroup)
Console.WriteLine("\t{0} {1}", student.FirstName, student.LastName);
}
}
/* Output:
Group by a boolean into two groups with string keys
"True" and "False" and project into a new anonymous type:
Key: True
Svetlana Omelchenko
Hugo Garcia
Sven Mortensen
Michael Tucker
Cesar Garcia
Hanying Feng
Lance Tucker
Key: False
Claire O'Donnell
Debra Garcia
*/
匿名型を使用して複数の値を含むキーをカプセル化する方法を次の例に示します。この例の 2 番目のキーの値は、最初の試験で学生が 85 点を超えたかどうかを示すブール値です。キーとして任意のプロパティを使用してグループを並べ替えることができます。
private static void GroupByCompositeKey()
{
var queryHighScoreGroups =
from student in students
group student by new { FirstLetter = student.LastName[0], Score = student.ExamScores[0] > 85 } into studentGroup
orderby studentGroup.Key.FirstLetter
select studentGroup;
Console.WriteLine("\r\nGroup and order by a compound key:");
foreach (var scoreGroup in queryHighScoreGroups)
{
string s = scoreGroup.Key.Score == true ? "more than" : "less than";
Console.WriteLine("Name starts with {0} who scored {1} 85", scoreGroup.Key.FirstLetter, s);
foreach (var item in scoreGroup)
{
Console.WriteLine("\t{0} {1}", item.FirstName, item.LastName);
}
}
}
/* Output:
Group and order by a compound key:
Name starts with F who scored more than 85
Hanying Feng
Name starts with G who scored more than 85
Hugo Garcia
Cesar Garcia
Name starts with G who scored less than 85
Debra Garcia
Name starts with M who scored more than 85
Sven Mortensen
Name starts with O who scored more than 85
Svetlana Omelchenko
Name starts with O who scored less than 85
Claire O'Donnell
Name starts with T who scored more than 85
Michael Tucker
Name starts with T who scored less than 85
Lance Tucker
*/
コードのコンパイル方法
次の例には、「方法 : オブジェクトのコレクションを照会する (C# プログラミング ガイド)」のサンプル アプリケーションで定義されているオブジェクトへの参照があります。このメソッドをコンパイルして実行するには、メソッドをそのアプリケーションの StudentClass クラスに貼り付け、Main メソッドからそのメソッドを呼び出すコードを追加します。
独自のアプリケーションに合わせてこのメソッドを変更する場合、LINQ には .NET Framework Version 3.5 が必要なこと、および System.Core.dll への参照と System.Linq の using ディレクティブをプロジェクトに含める必要があることに注意してください。LINQ to SQL、LINQ to XML、および LINQ to DataSet の場合は、追加の using と参照も必要です。詳細については、「方法 : LINQ プロジェクトを作成する」を参照してください。
参照
処理手順
方法 : グループ化操作でサブクエリを実行する (C# プログラミング ガイド)
方法 : グループをグループ化する (C# プログラミング ガイド)