前言
在 C# 的 LINQ 查询中,Select 和 SelectMany 是处理集合或可查询对象的常用方法,它们虽有相似之处,但也存在显著区别。
Select 方法用于将集合中的每个元素映射(投影)到一个新的形式,生成的序列长度与原集合相同。例如,若有一个整数集合,通过 Select 可将每个整数映射为对应的字符串形式,结果序列的元素数量与原集合一致。
而 SelectMany 方法则针对集合中的每个元素返回一个可枚举序列,随后将这些子序列"压平"(Flatten),合并为一个新的序列。
以一个包含多个子列表的列表为例,使用 SelectMany 可将所有子列表中的元素整合到一个单一的序列中,便于进一步处理和操作。
Select 示例
以下例子演示如何使用 Select 将字符串列表中的每个元素转换成大写形式:
public class Program
{
public static void Main()
{
var fruits = new List<string> { "apple", "banana", "cherry" };
// 使用 Select 把每个字符串都转换为大写
var upperFruits =
fruits.Select(fruit => fruit.ToUpper());
foreach (var item in upperFruits)
{
Console.WriteLine(item);
}
}
}
fruits.Select(...)将原始字符串映射为新的大写字符串。
结果数量和 fruits中的元素数量相同,只有值发生了改变。
SelectMany 示例
下面示例演示如何使用 SelectMany将一个"列表的列表"展平成单个列表。
假设我们有三位学生,每位学生拥有多门课程:
public class Program
{
public static void Main()
{
var studentsCourses = new List<List<string>>
{
new List<string> { "Math", "Physics" },
new List<string> { "History", "Literature", "Philosophy" },
new List<string> { "ComputerScience", "Statistics" }
};
// 使用 SelectMany 将多层列表进行扁平化
var flattenedCourses = studentsCourses.SelectMany(courses => courses);
foreach (var course in flattenedCourses)
{
Console.WriteLine(course);
}
}
}
在这个示例中:
SelectMany对每个子列表执行投影,然后将所有子列表合并成一个单一序列。
与Select的区别在于,如果使用Select,最终结果会是一个列表(外层)里装着多个列表(内层);
而SelectMany则直接展开为单层列表。
SelectMany 实例
将某一个月的项目自动补齐日期
namespace AppSelectLinq
{
public class ProjectData
{
public DateTime Date { get; set; }
public string Project { get; set; }
public int Qty { get; set; }
}
public class Program
{
public static void Main()
{
// 假设的日期范围
var monthRange = Tuple.Create(
new DateTime(2024, 12, 1), new DateTime(2024, 12, 31));
// 假设的项目数据列表
var lst = new List<ProjectData>
{
new ProjectData
{ Date = new DateTime(2024, 12, 1), Project = "Project A", Qty = 10 },
new ProjectData
{ Date = new DateTime(2024, 12, 1), Project = "Project B", Qty = 5 },
new ProjectData
{ Date = new DateTime(2024, 12, 2), Project = "Project A", Qty = 8 },
new ProjectData
{ Date = new DateTime(2024, 12, 3), Project = "Project C", Qty = 12 },
};
DateTime start2 = monthRange.Item1.Date;
DateTime end2 = monthRange.Item2.Date;
var allDates = Enumerable.Range(0, (end2 - start2).Days + 1)
.Select
(offset => start2.AddDays(offset))
.SelectMany
(date => lst.Select(x => x.Project).Distinct()
.Select(proj => new
{
Date = date,
Project = proj,
Qty = lst.FirstOrDefault
(x => x.Date == date && x.Project == proj)?.Qty ?? 0
}))
.ToList();
// 输出结果
foreach (var item in allDates)
{
Console.WriteLine($"Date: {item.Date.ToShortDateString()}, Project: {item.Project}, Qty: {item.Qty}");
}
}
}
}
定义了一个日期范围monthRange。
创建了一个示例项目数据列表 lst,其中包含不同日期和项目的数量。
使用 Enumerable.Range生成从start2 到end2 的所有日期。
对于每个日期,使用 SelectMany结合 Distinct方法获取所有项目,并为每个项目创建一个匿名对象,包含日期、项目名称和数量(如果没有找到,则数量为 0)
总结
1、Select
一对一映射:对原序列中的每个元素进行转换,生成一个新元素,新元素与原元素一一对应。
长度不变:返回的结果序列长度与原序列相同,元素数量保持一致。
2、SelectMany
一对多映射:原序列中的每个元素可映射为一个子序列,子序列中可包含多个元素。
扁平化处理:将所有子序列合并、压平,最终返回一个单层序列,便于统一处理。
3、重要性
这两个方法在 LINQ 中极为常用,合理运用它们,能让你在处理复杂数据结构时,代码更加简洁、可读性更高,有效提升开发效率和代码质量。
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
作者:技术老小子
出处:mp.weixin.qq.com/s/18885o92RrJigsjvTqDPhg
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!