繁体   English   中英

使用 LINQ 根据 C# 中的属性值搜索嵌套在另一个列表中的列表中的项目?

[英]Searching for an item in a list that's nested inside another list based on a property value in C# using LINQ?

有没有办法使用 LINQ 根据属性值在嵌套在另一个列表中的列表中搜索项目?

鉴于下面的模型,对于给定的订单(变量customerOrder ),我想返回最早的订单日期( Date ),其中 Day 是“Sunday”。

楷模:

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();

编辑更优雅的查询

您可以使用 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 class 不是必需的。 DateTime类型有一个属性DayOfWeek ,您可以使用它来测试 Date 是否为 Sunday。

DayOfWeek是一个枚举,因此您可以简单地测试MyDate.DayOfWeek == DayOfWeek.Sunday而不是为此目的依赖string

首先,您的代码将无法按预期工作。 dates.OrderBy(d => d.Date); 不起作用: OrderBy返回一个 IEnumerable,它不会更改原始集合。 您应该使用List.Sort ` 或这样做:

dates = dates.OrderBy(d => d.Date).ToList()

其次,您使用FirstOrDefault :它有一个接受谓词进行搜索的重载; 所以不需要Where调用。 此外,如果没有找到,FirstOrDefault 将返回null 如果这是可能的情况,您应该考虑检查orderDate是否为 null:

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紧凑的方式,你可以用这样的东西 go :

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 来做这些事情。

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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM