C# LINQ group by
上次修改时间:2025 年 4 月 30 日
在本文中,我们将展示如何使用 LINQ 查询在 C# 中对数据进行分组。
语言集成查询 (LINQ) 是 C# 中一种强大的领域特定语言,它可以简化跨各种来源的数据查询和操作。 无论是处理数组、列表、XML 文件还是复杂的数据库,LINQ 都提供了一种统一的语法和与编程语言的无缝集成,使开发人员可以直接在代码中编写可读、富有表现力且高效的查询。
LINQ 中的 group、by 和 into 关键字允许您根据特定条件将数据组织到集合中。 这些关键字使您可以轻松地在 C# 中有效地分类和操作大型数据集。 然后可以进一步处理或查询分组数据以获得见解。
group by 操作是 LINQ 中的一个强大功能,它根据共享的特征或条件将数据聚合到组中。 每个组都由一个“键”定义,该键充当组的标识符,而组中的项共享共同的属性或满足给定的表达式。 此操作通常用于总结、排序或分析结构化数据。
使用 LINQ,可以按以下方式对数据进行分组:
- 单个键:根据单个属性(例如类别或 ID)对项进行分组。
- 属性:根据特定属性(例如颜色或类型)对数据进行分组。
- 复合键:使用多个属性作为键对项目进行分组,组合日期和位置等属性。
- 计算的数字范围:将数据分组为范围或存储桶,例如年龄段或价格范围。
- 表达式:使用复杂的条件或计算来定义分组逻辑。
group 关键字指定要分组的数据,by 标识分组标准,而 into 允许进一步处理或查询分组数据。
C# LINQ group by 键
我们可以根据某个键将数据分组到不同的类别中。
List<Car> cars =
[
new ("Audi", "red", 52642),
new ("Mercedes", "blue", 57127),
new ("Skoda", "black", 9000),
new ("Volvo", "red", 29000),
new ("Bentley", "yellow", 350000),
new ("Citroen", "white", 21000),
new ("Hummer", "black", 41400),
new ("Volkswagen", "white", 21600),
];
var groups = from car in cars
group car by car.Colour;
foreach (var group in groups)
{
Console.WriteLine(group.Key);
foreach (var car in group)
{
Console.WriteLine($" {car.Name} {car.Price}");
}
}
record Car(string Name, string Colour, int Price);
我们根据汽车的颜色将汽车分成几组。
$ dotnet run red Audi 52642 Volvo 29000 blue Mercedes 57127 black Skoda 9000 Hummer 41400 yellow Bentley 350000 white Citroen 21000 Volkswagen 21600
C# LINQ group by 属性
我们可以按元素的某些属性对数据进行分组。
List<string> words =
[
"war", "water", "cup", "cloud", "atom", "abyss", "soup", "book",
"moon", "nice", "sky", "forest"
];
var groups = from word in words
group word by word.Length;
foreach (var group in groups)
{
Console.WriteLine(group.Key);
foreach (var e in group)
{
Console.WriteLine($"{e}");
}
Console.WriteLine("----------------");
}
该示例按单词的长度对单词进行分组。
$ dotnet run 3 war cup sky ---------------- 5 water cloud abyss ---------------- 4 atom soup book moon nice ---------------- 6 forest ----------------
C# LINQ group by 并聚合
在下面的示例中,我们执行分组和聚合操作。
Revenue[] revenues =
[
new (1, "Q1", 2340),
new (2, "Q1", 1200),
new (3, "Q1", 980),
new (4, "Q2", 340),
new (5, "Q2", 780),
new (6, "Q3", 2010),
new (7, "Q3", 3370),
new (8, "Q4", 540),
];
var res = from revenue in revenues
group revenue by revenue.Quarter
into g
select new { Quarter = g.Key, Total = g.Sum(e => e.Amount) };
foreach (var line in res)
{
Console.WriteLine(line);
}
record Revenue(int Id, string Quarter, int Amount);
我们有四个季度的收入。我们按季度对收入进行分组,并对金额求和。
$ dotnet run
{ Quarter = Q1, Total = 4520 }
{ Quarter = Q2, Total = 1120 }
{ Quarter = Q3, Total = 5380 }
{ Quarter = Q4, Total = 540 }
我们可以使用 where 子句对聚合后的数据应用筛选器。
Revenue[] revenues =
[
new (1, "Q1", 2340),
new (2, "Q1", 1200),
new (3, "Q1", 980),
new (4, "Q2", 340),
new (5, "Q2", 780),
new (6, "Q3", 2010),
new (7, "Q3", 3370),
new (8, "Q4", 540),
];
var res = from revenue in revenues
group revenue by revenue.Quarter
into g
where g.Count() == 2
select new { Quarter = g.Key, Total = g.Sum(c => c.Amount) };
foreach (var line in res)
{
Console.WriteLine(line);
}
record Revenue(int Id, string Quarter, int Amount);
在此示例中,我们只选择那些恰好有两条收入记录的季度。
$ dotnet run
{ Quarter = Q2, Total = 1120 }
{ Quarter = Q3, Total = 5380 }
C# LINQ group by 复合键
我们可以按复合键(由多个字段组成)对数据进行分组。
User[] users =
[
new ("John", "Doe", "gardener"),
new ("Jane", "Doe", "teacher"),
new ("Roger", "Roe", "driver"),
new ("Peter", "Doe", "teacher"),
new ("Pavol", "Novak", "programmer"),
new ("Albert", "Novak", "teacher"),
new ("Sam", "Novak", "driver"),
new ("Peter", "Horvath", "accountant"),
new ("Lucia", "Horvath", "accountant"),
new ("Michael", "Novak", "programmer"),
];
var groups = from user in users
group user by new { user.LastName, user.Occupation };
foreach (var group in groups)
{
Console.WriteLine(group.Key);
foreach (var e in group)
{
Console.WriteLine($"{e}");
}
Console.WriteLine("----------------");
}
record User(string FirstName, string LastName, string Occupation);
该程序按用户的姓氏和职业安排用户组。
$ dotnet run
{ LastName = Doe, Occupation = gardener }
User { FirstName = John, LastName = Doe, Occupation = gardener }
----------------
{ LastName = Doe, Occupation = teacher }
User { FirstName = Jane, LastName = Doe, Occupation = teacher }
User { FirstName = Peter, LastName = Doe, Occupation = teacher }
----------------
{ LastName = Roe, Occupation = driver }
User { FirstName = Roger, LastName = Roe, Occupation = driver }
----------------
{ LastName = Novak, Occupation = programmer }
User { FirstName = Pavol, LastName = Novak, Occupation = programmer }
User { FirstName = Michael, LastName = Novak, Occupation = programmer }
----------------
{ LastName = Novak, Occupation = teacher }
User { FirstName = Albert, LastName = Novak, Occupation = teacher }
----------------
{ LastName = Novak, Occupation = driver }
User { FirstName = Sam, LastName = Novak, Occupation = driver }
----------------
{ LastName = Horvath, Occupation = accountant }
User { FirstName = Peter, LastName = Horvath, Occupation = accountant }
User { FirstName = Lucia, LastName = Horvath, Occupation = accountant }
----------------
C# LINQ group by 布尔表达式
在以下示例中,我们使用布尔表达式对数据进行分组。
Student[] students =
[
new ("John", "Doe", 78),
new ("Roger", "Roe", 89),
new ("Peter", "Doe", 90),
new ("Pavol", "Novak", 34),
new ("Albert", "Novak", 66),
new ("Peter", "Horvath", 89),
new ("Lucia", "Horvath", 88),
new ("Michael", "Novak", 99),
];
var groups = from student in students
group student by new
{
Passed = student.Score > 70,
};
foreach (var group in groups)
{
if (group.Key.Passed)
{
Console.WriteLine("passed");
}
else
{
Console.WriteLine("failed");
}
foreach (var e in group)
{
Console.WriteLine(e);
}
Console.WriteLine("----------------------------");
}
record Student(string FirstName, string LastName, int Score);
我们有一个学生记录数组,其中包含名字、姓氏和分数字段。 要通过考试,学生需要获得 70 分以上。 使用 LINQ 表达式,我们将学生分为两组:通过和失败。
$ dotnet run
passed
Student { FirstName = John, LastName = Doe, Score = 78 }
Student { FirstName = Roger, LastName = Roe, Score = 89 }
Student { FirstName = Peter, LastName = Doe, Score = 90 }
Student { FirstName = Peter, LastName = Horvath, Score = 89 }
Student { FirstName = Lucia, LastName = Horvath, Score = 88 }
Student { FirstName = Michael, LastName = Novak, Score = 99 }
----------------------------
failed
Student { FirstName = Pavol, LastName = Novak, Score = 34 }
Student { FirstName = Albert, LastName = Novak, Score = 66 }
----------------------------
C# LINQ group by 范围
在下一个示例中,我们使用数字范围作为组键。
List<Student> students =
[
new ("John", "Doe", 78),
new ("Roger", "Roe", 89),
new ("Peter", "Doe", 90),
new ("Pavol", "Novak", 34),
new ("Albert", "Novak", 66),
new ("Peter", "Horvath", 89),
new ("Lucia", "Horvath", 88),
new ("Michael", "Novak", 99),
];
var groups = from std in students
let avg = students.Average(e => e.Score)
group std by (std.Score / 10) into g
orderby g.Key
select g;
foreach (var group in groups)
{
Console.WriteLine(group.Key * 10);
foreach (var e in group)
{
Console.WriteLine(e);
}
Console.WriteLine("----------------------------");
}
record Student(string FirstName, string LastName, int Score);
该示例将学生分组到百分位范围内。
$ dotnet run
30
Student { FirstName = Pavol, LastName = Novak, Score = 34 }
----------------------------
60
Student { FirstName = Albert, LastName = Novak, Score = 66 }
----------------------------
70
Student { FirstName = John, LastName = Doe, Score = 78 }
----------------------------
80
Student { FirstName = Roger, LastName = Roe, Score = 89 }
Student { FirstName = Peter, LastName = Horvath, Score = 89 }
Student { FirstName = Lucia, LastName = Horvath, Score = 88 }
----------------------------
90
Student { FirstName = Peter, LastName = Doe, Score = 90 }
Student { FirstName = Michael, LastName = Novak, Score = 99 }
----------------------------
C# LINQ 词频统计
在以下示例中,我们计算文件中单词的频率。
$ wget https://raw.githubusercontent.com/janbodnar/data/main/the-king-james-bible.txt
我们从詹姆斯国王钦定本圣经中读取数据。
using System.Text.RegularExpressions;
var fileName = "the-king-james-bible.txt";
var text = File.ReadAllText(fileName);
var dig = new Regex(@"\d");
var matches = new Regex("[a-z-A-Z']+").Matches(text);
var words =
from match in matches
let val = match.Value
where !dig.IsMatch(val)
select match.Value;
var topTen =
(from word in words
group word by word into wg
orderby wg.Count() descending
select new {word = wg.Key, Total = wg.Count()}
).Take(10);
foreach (var e in topTen)
{
Console.WriteLine($"{e.word}: {e.Total}");
}
我们统计《英王钦定本圣经》中单词的频率。
var matches = new Regex("[a-z-A-Z']+").Matches(text);
var words = matches.Select(m => m.Value).ToList();
我们使用 Matches 方法找到所有匹配项。从匹配集合中,我们将所有单词放入一个列表中。
var words =
from match in matches
let val = match.Value
where !dig.IsMatch(val)
select match.Value;
在第一个查询中,我们找到所有匹配项。 从匹配集合中,我们获取所有单词。
var topTen =
(from word in words
group word by word into wg
orderby wg.Count() descending
select new {word = wg.Key, Total = wg.Count()}
).Take(10);
单词按频率降序分组和排序。 我们采用前十个最常见的单词。
$ dotnet run the 62103 and 38848 of 34478 to 13400 And 12846 that 12576 in 12331 shall 9760 he 9665 unto 8942
来源
在本文中,我们在 C# LINQ 中对数据进行了分组。
作者
列出所有 C# 教程。