使用 LINQ 根据 C# 中的属性值搜索嵌套在另一个列表中的列表中的项目?
Posted
技术标签:
【中文标题】使用 LINQ 根据 C# 中的属性值搜索嵌套在另一个列表中的列表中的项目?【英文标题】:Searching for an item in a list that's nested inside another list based on a property value in C# using LINQ? 【发布时间】:2021-12-28 16:43:03 【问题描述】:有没有办法使用 LINQ 根据属性值搜索嵌套在另一个列表中的列表中的项目?
鉴于以下模型,对于给定的订单(变量customerOrder
),我想返回最早的订单日期(Date
),其中日期是“星期日”。
型号:
public class Order
public int Id get; set;
public List<OrderLine> OrderLines get; set;
public class OrderLine
public string Description get; set;
public List<OrderDate> OrderDates get; set;
public class OrderDate
public DateTime Date get; set;
public string Day get; set;
代码:
var dates = new List<DateTime>();
foreach(var a in customerOrder.OrderLines)
var orderDate = a.OrderDates.Where(x => x.DateTypeId.Equals("Sunday")).FirstOrDefault();
dates.Add(orderDate.ActualDate);
dates.OrderBy(d => d.Date);
return dates.FirstOrDefault();
【问题讨论】:
【参考方案1】:编辑 更优雅的查询
您可以使用 Linq 来实现您的结果。
这是一个与您的代码非常相似的查询。
customerOrder.OrderLines
.Select(ol => ol.OrderDates
.Where(x => x.Day.Equals("Sunday"))
.FirstOrDefault())
.Where(d => d != null)
.OrderBy(d => d.Date)
.FirstOrDefault();
可以更优雅地改写为:
customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.OrderBy(d => d.Date)
.FirstOrDefault(d => d.Day == "Sunday");
这是一个带有一些测试数据和转储的 Linqpad 查询供您尝试。 只需复制并粘贴到 Linqpad 中。
void Main()
var customerOrder = new Order
Id = 1,
OrderLines = Enumerable
.Range(0, 10)
.Select(i => new OrderLine
Description = $"Line Description i",
OrderDates = Enumerable.Range(0, 10)
.Select(j => new OrderDate
Date = DateTime.Now.AddDays(i+j),
Day = DateTime.Now.AddDays(i+j).DayOfWeek.ToString()
)
.ToList()
)
.ToList()
.Dump();
customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.OrderBy(d => d.Date)
.FirstOrDefault(d => d.Day == "Sunday")
.Dump();
// You can define other methods, fields, classes and namespaces here
public class Order
public int Id get; set;
public List<OrderLine> OrderLines get; set;
public class OrderLine
public string Description get; set;
public List<OrderDate> OrderDates get; set;
public class OrderDate
public DateTime Date get; set;
public string Day get; set;
另一方面,OrderDate
类不是必需的。 DateTime
类型有一个属性 DayOfWeek
,您可以使用它来测试 Date is a Sunday。
DayOfWeek
是一个枚举,因此您可以简单地测试MyDate.DayOfWeek == DayOfWeek.Sunday
,而不是为此目的依赖string
。
【讨论】:
在效率方面与我的方法相比如何,或者可以忽略不计? 我会说它应该是可比的。但是,如果您真的想确定,则需要对其进行基准测试。我使用 BenchmarkDotNet 进行此类活动:github.com/dotnet/BenchmarkDotNet。但除非有充分的理由,否则我真的不会担心性能。 @Alexander 也提出了一个很好的观点,即 Linq 查询不会改变所查询的数据(请参阅他的答案的开头)。他指出了您实施的一个错误(@Alenxander,我对您的回答做了 +1,并根据您和@JoshuaRobinson 编辑了我的回答以提出更优雅的要求)。【参考方案2】:首先,您的代码将无法按预期工作。
dates.OrderBy(d => d.Date);
不起作用:OrderBy
返回一个 IEnumerable,它不会更改原始集合。您应该使用List.Sort
` 或这样做:
dates = dates.OrderBy(d => d.Date).ToList()
其次,您使用FirstOrDefault
:它有一个接受谓词进行搜索的重载;所以不需要Where
调用。此外,如果没有找到,FirstOrDefault 将返回 null
。如果这是一种可能的情况,您应该考虑检查orderDate
是否为空:
var dates = new List<DateTime>();
foreach(var a in customerOrder.OrderLines)
var orderDate = a.OrderDates.FirstOrDefault(x => x.DateTypeId.Equals("Sunday"));
if (orderDate is )
dates.Add(orderDate.ActualDate);
dates = dates.OrderBy(d => d.Date).ToList();
return dates.FirstOrDefault();
这应该可以正常工作。但是很难猜测代码示例的行为的哪些方面是有意的,哪些不是。您询问搜索,但对 OrderBy 部分只字未提。请您澄清一下这部分吗?
回答这个问题,如果 better
你的意思是更紧凑的方式,你可以这样:
var result = customerOrder.OrderLines
.SelectMany(a => a.OrderDates)
.OrderBy(d => d.Date)
.FirstOrDefault(x => x.DateTypeId.Equals("Sunday"));
return result;
你现在不应该被better way
困扰;首先你应该至少从working way
开始。我建议你学习如何使用 Linq 和不使用 Linq 来做这些事情。
【讨论】:
【参考方案3】:Better 有点主观,但您可以使用Enumerable.SelectMany
扩展方法将OrderDate
实例扁平化为一个序列。
然后您可以使用Enumerable.Where
扩展方法过滤"Sunday"
的日期。
那么你可以使用Enumerable.Min
扩展方法来获取最小日期。
所有这些都可以链接到一个语句中。
DateTime earliestSunday = customeOrder
.OrderLines
.SelectMany(ol => ol.OrderDates)
.Where(od => od.Day == "Sunday")
.Min(od => od.Date);
【讨论】:
注意如果集合不包含任何元素,Min
调用会抛出异常。以上是关于使用 LINQ 根据 C# 中的属性值搜索嵌套在另一个列表中的列表中的项目?的主要内容,如果未能解决你的问题,请参考以下文章
C# 如何在另一个 Linq 语句中的 XmlAttributeCollection 上使用 Linq?
从列中获取值并根据值是不是正确显示该行中的所有内容(LINQ - c# web API)